Práctico 02. Regresión lineal simple

Métodos estadísticos para Ciencias Sociales III

Fecha de publicación

12 de agosto de 2026

1 Presentación

1.1 Objetivo de la práctica

En el práctico anterior trabajamos con la estructura de una base de datos, las escalas de medición y la asociación entre variables, y trazamos una primera recta de regresión sin detenernos en cómo se obtiene.

Esta guía se concentra exclusivamente en la regresión lineal simple, es decir, en modelos con una sola variable independiente. Al terminar, usted debería ser capaz de:

  1. Comprender la recta de regresión como una síntesis de las medias condicionales de \(Y\)
  2. Estimar los coeficientes \(b_0\) y \(b_1\) a mano, replicando el procedimiento visto en clases
  3. Estimar el mismo modelo con la función lm() y comprobar que los resultados coinciden
  4. Interpretar el intercepto y la pendiente en términos sustantivos
  5. Obtener valores predichos y residuos, y utilizarlos para predecir
  6. Comprender por qué el criterio de estimación se denomina mínimos cuadrados ordinarios

La evaluación del ajuste del modelo (\(R^2\)) y la inferencia estadística se abordarán en la sesión teórica siguiente y en el próximo práctico.

1.2 Datos

Continuaremos con la primera ola (2016) del Estudio Longitudinal Social de Chile (ELSOC), del Centro de Estudios de Conflicto y Cohesión Social (COES), y con la confianza en vecinos como variable dependiente.

2 Preparación

2.1 Librerías

pacman::p_load(dplyr,
               sjmisc,
               sjPlot,
               sjlabelled,
               kableExtra,
               ggplot2)
rm(list = ls())       # limpiar el espacio de trabajo
options(scipen = 999) # valores sin notación científica

2.2 Carga y procesamiento

Repetimos el procesamiento realizado en el práctico anterior. Conviene que ejecute cada línea y verifique que comprende qué hace; si alguna no le resulta clara, revise el práctico 01 antes de continuar.

#load(url("https://dataverse.harvard.edu/api/access/datafile/7245118"))
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(t01,      # Confianza en vecinos
         m01,      # Nivel educacional
         m0_sexo,  # Sexo
         m0_edad)  # Edad

proc_data <- proc_data %>%
  set_na(., na = c(-999, -888, -777, -666)) %>%
  rename("confianza_vecinos" = t01,
         "educacion" = m01,
         "sexo" = m0_sexo,
         "edad" = m0_edad)

proc_data <- na.omit(proc_data) # trabajaremos solo con casos completos

dim(proc_data)
[1] 2906    4

2.3 Descriptivos

Antes de estimar cualquier modelo, conviene tener presentes los descriptivos de las variables involucradas. Como veremos, los promedios reaparecerán en el cálculo de los coeficientes.

proc_data %>%
  summarise(media_confianza = mean(confianza_vecinos),
            de_confianza = sd(confianza_vecinos),
            media_edad = mean(edad),
            de_edad = sd(edad)) %>%
  kable("markdown", digits = 2)
media_confianza de_confianza media_edad de_edad
3.2 1.2 46.1 15.3

3 De las medias condicionales a la recta

Antes de estimar la recta, conviene detenerse en la idea que está detrás de ella.

Si no tuviéramos ninguna variable independiente, nuestra mejor predicción de la confianza para cualquier persona sería el promedio general de la variable:

mean(proc_data$confianza_vecinos)
[1] 3.195802

Pero sí tenemos información adicional: conocemos la edad de cada persona. La pregunta entonces es si el promedio de confianza cambia según el valor de la edad. Ese promedio calculado por separado para cada valor de \(X\) es lo que llamamos media condicional: el promedio de \(Y\) condicionado a un valor determinado de \(X\).

3.1 Cálculo de las medias condicionales

medias_cond <- proc_data %>%
  group_by(edad) %>%
  summarise(media_confianza = mean(confianza_vecinos),
            casos = n())

head(medias_cond, 10) %>%
  kable("markdown", digits = 2)
edad media_confianza casos
18 2.74 19
19 3.09 32
20 3.04 26
21 3.15 39
22 2.88 49
23 2.93 44
24 2.98 51
25 2.64 45
26 3.02 44
27 3.20 51

Cada fila responde a una pregunta distinta: entre las personas de 18 años, ¿cuál es el promedio de confianza? ¿Y entre las de 19? Note además la columna de casos: algunas edades tienen muchas observaciones y otras muy pocas, lo que hace que sus promedios sean más o menos estables.

3.2 Visualización

Grafiquemos esas medias condicionales, con el tamaño de cada punto proporcional al número de casos que lo respalda:

ggplot(medias_cond, aes(x = edad, y = media_confianza)) +
  geom_point(aes(size = casos), color = "#4f97ff", alpha = 0.7) +
  geom_hline(yintercept = mean(proc_data$confianza_vecinos),
             linetype = "dotted", color = "grey40") +
  labs(x = "Edad", y = "Media condicional de confianza",
       size = "Casos") +
  theme_minimal()
Figura 1: Media condicional de confianza en vecinos según edad

La línea punteada marca el promedio general. Si la edad no aportara información, las medias condicionales se ubicarían aleatoriamente en torno a esa línea. Lo que observamos, en cambio, es una tendencia: las medias condicionales de las edades mayores tienden a situarse por encima.

3.3 Reduciendo el ruido

Los promedios calculados sobre pocos casos oscilan bastante. Una forma de apreciar mejor la tendencia consiste en agrupar la edad en tramos:

proc_data$tramo_edad <- cut(proc_data$edad,
                            breaks = c(17, 29, 39, 49, 59, 69, 100),
                            labels = c("18-29", "30-39", "40-49",
                                       "50-59", "60-69", "70+"))

proc_data %>%
  group_by(tramo_edad) %>%
  summarise(media_confianza = mean(confianza_vecinos),
            casos = n()) %>%
  kable("markdown", digits = 2)
tramo_edad media_confianza casos
18-29 3.01 504
30-39 3.09 558
40-49 3.25 587
50-59 3.23 625
60-69 3.30 414
70+ 3.44 218
Figura 2: Media condicional de confianza en vecinos según tramo de edad
proc_data %>%
  group_by(tramo_edad) %>%
  summarise(media_confianza = mean(confianza_vecinos)) %>%
  ggplot(aes(x = tramo_edad, y = media_confianza)) +
  geom_col(fill = "#04245f", alpha = 0.85) +
  geom_hline(yintercept = mean(proc_data$confianza_vecinos),
             linetype = "dotted", color = "#c0392b", linewidth = 1) +
  labs(x = "Tramo de edad", y = "Media condicional de confianza") +
  theme_minimal()

3.4 La recta como síntesis

Finalmente, superpongamos la recta de regresión a las medias condicionales:

ggplot(medias_cond, aes(x = edad, y = media_confianza)) +
  geom_point(aes(size = casos), color = "#4f97ff", alpha = 0.7) +
  geom_smooth(data = proc_data,
              aes(x = edad, y = confianza_vecinos),
              method = "lm", se = FALSE,
              color = "#04245f", linewidth = 1.2) +
  labs(x = "Edad", y = "Media condicional de confianza", size = "Casos") +
  theme_minimal()
Figura 3: Medias condicionales y recta de regresión

Aquí está la idea central de la sesión anterior: la recta de regresión es una síntesis de las medias condicionales. No pasa exactamente por cada una de ellas, sino que resume su tendencia general mediante dos números, el intercepto y la pendiente.

Esa síntesis tiene un costo y una ganancia. El costo es que perdemos las particularidades de cada valor de \(X\). La ganancia es doble: obtenemos una descripción parsimoniosa de la relación, y podemos predecir valores de \(Y\) para valores de \(X\) que no observamos directamente en la muestra.

4 Estimación manual de los coeficientes

Nuestra pregunta es: ¿en qué medida la edad se asocia a la confianza en los vecinos? Por lo tanto:

  • \(Y\) = confianza en vecinos (variable dependiente)
  • \(X\) = edad (variable independiente)

Recordemos las fórmulas vistas en clases:

\[b_{1}=\frac{\sum_{i=1}^{n}(x_i - \bar{x})(y_i - \bar{y})} {\sum_{i=1}^{n}(x_i - \bar{x})^2} \qquad b_{0}=\bar{Y}-b_{1}\bar{X}\]

4.1 Paso 1: diferencias respecto del promedio

Toda la estimación se construye a partir de la distancia de cada caso respecto del promedio de su variable:

proc_data$difx <- proc_data$edad - mean(proc_data$edad)
proc_data$dify <- proc_data$confianza_vecinos - mean(proc_data$confianza_vecinos)

4.2 Paso 2: productos cruzados y diferencias al cuadrado

proc_data$dif_cru <- proc_data$difx * proc_data$dify  # covariación
proc_data$difx2 <- proc_data$difx^2                   # varianza de X

Revisemos los primeros casos:

head(proc_data %>%
       select(edad, confianza_vecinos, difx, dify, dif_cru, difx2)) %>%
  kable("markdown", digits = 2)
edad confianza_vecinos difx dify dif_cru difx2
64 1 17.9 -2.2 -39.31 320.50
60 3 13.9 -0.2 -2.72 193.28
26 3 -20.1 -0.2 3.94 403.90
51 3 4.9 -0.2 -0.96 24.04
69 2 22.9 -1.2 -27.39 524.53
62 2 15.9 -1.2 -19.02 252.89

Note el sentido de la columna dif_cru: es positiva cuando ambas variables se desvían del promedio en la misma dirección (una persona mayor que el promedio y con confianza mayor que el promedio, o bien menor en ambas), y negativa cuando lo hacen en direcciones opuestas. La suma de esa columna resume, entonces, si las variables tienden a moverse juntas.

4.3 Paso 3: las sumas

suma_cru <- sum(proc_data$dif_cru)
suma_x2 <- sum(proc_data$difx2)

c(suma_productos_cruzados = suma_cru, suma_difx2 = suma_x2)
suma_productos_cruzados              suma_difx2 
               5404.588              680305.440 

4.4 Paso 4: los coeficientes

b1 <- suma_cru / suma_x2
b0 <- mean(proc_data$confianza_vecinos) - b1 * mean(proc_data$edad)

c(b0 = b0, b1 = b1)
         b0          b1 
2.829587789 0.007944355 

Con lo cual la ecuación de nuestro modelo es:

\[\widehat{confianza} = b_0 + b_1 \times edad\]

5 Estimación con lm()

Naturalmente, no calcularemos los coeficientes a mano cada vez. La función lm() (de linear model) hace exactamente el mismo procedimiento. Su sintaxis se lee como “confianza en vecinos en función de edad”:

NotaRegresión simple

modelo <- lm(y ~ x, data = datos)

modelo <- lm(confianza_vecinos ~ edad, data = proc_data)
coef(modelo)
(Intercept)        edad 
2.829587789 0.007944355 

Compare estos valores con los que obtuvo en el paso 4: son idénticos. La función lm() no hace magia; ejecuta el mismo cálculo que usted acaba de realizar.

6 Interpretación

6.1 La pendiente

coef(modelo)[2]
       edad 
0.007944355 

La pendiente indica cuánto cambia la confianza esperada por cada año adicional de edad. Al ser positiva, la confianza tiende a ser mayor entre las personas de más edad.

Dos precisiones importantes al momento de redactar la interpretación:

  • Se trata de un cambio promedio, no de una afirmación sobre cada persona
  • Es una asociación, no una relación causal: no estamos afirmando que envejecer produzca confianza

6.2 El intercepto

coef(modelo)[1]
(Intercept) 
   2.829588 

El intercepto es el valor esperado de confianza cuando la edad es cero. Aquí aparece un problema frecuente: en esta base no existe ninguna persona de 0 años, de modo que el intercepto no tiene una interpretación sustantiva directa.

range(proc_data$edad)
[1] 18 88

7 Valores predichos y predicción

7.1 Valores predichos para cada caso

Para cada persona de la base, el modelo genera un valor predicho \(\widehat{Y}\):

proc_data$predicho <- fitted.values(modelo)

head(proc_data %>% select(edad, confianza_vecinos, predicho)) %>%
  kable("markdown", digits = 2)
edad confianza_vecinos predicho
64 1 3.34
60 3 3.31
26 3 3.04
51 3 3.23
69 2 3.38
62 2 3.32

7.2 Predicción para valores específicos

También podemos preguntar por valores de \(X\) que nos interesen, aunque no correspondan a un caso particular de la base. Por ejemplo, ¿cuál es la confianza esperada para personas de 30, 50 y 70 años?

Aplicando la ecuación directamente:

b0 + b1 * c(30, 50, 70)
[1] 3.067918 3.226806 3.385693

O bien con la función predict(), que es la forma habitual de hacerlo:

predict(modelo, newdata = data.frame(edad = c(30, 50, 70)))
       1        2        3 
3.067918 3.226806 3.385693 

Note que la diferencia entre la predicción para 30 y para 50 años equivale a 20 veces la pendiente. Esa es la propiedad central de un modelo lineal: el efecto de \(X\) es constante a lo largo de todo su rango.

8 Residuos

La diferencia entre el valor observado y el valor predicho es el residuo:

\[e_i = Y_i - \widehat{Y}_i\]

proc_data$residuo <- residuals(modelo)

head(proc_data %>% select(edad, confianza_vecinos, predicho, residuo)) %>%
  kable("markdown", digits = 2)
edad confianza_vecinos predicho residuo
64 1 3.34 -2.34
60 3 3.31 -0.31
26 3 3.04 -0.04
51 3 3.23 -0.23
69 2 3.38 -1.38
62 2 3.32 -1.32

Un residuo positivo indica que la persona reporta más confianza de la que el modelo predice para su edad; uno negativo, menos. Graficamente, corresponde a la distancia vertical entre el punto y la recta:

set.seed(2026)
sub <- proc_data[sample(nrow(proc_data), 20), ]

ggplot(sub, aes(x = edad, y = confianza_vecinos)) +
  geom_smooth(data = proc_data, method = "lm", se = FALSE,
              color = "#04245f", linewidth = 1.2) +
  geom_segment(aes(xend = edad, yend = predicho),
               color = "#c0392b", linetype = "dashed") +
  geom_point(size = 2.5, color = "#4f97ff") +
  labs(x = "Edad", y = "Confianza en vecinos") +
  theme_minimal()
Figura 4: Valores observados, valores predichos y residuos (20 casos)

9 ¿Por qué “mínimos cuadrados”?

En clases señalamos que por una misma nube de puntos pueden trazarse infinitas rectas, y que el criterio OLS selecciona aquella que minimiza la suma de los residuos al cuadrado. Comprobémoslo.

Primero, note que la suma de los residuos del modelo estimado es prácticamente cero, porque los residuos positivos y negativos se cancelan entre sí. Por eso no sirve como criterio:

sum(proc_data$residuo)
[1] -0.0000000000006293993

En cambio, si elevamos los residuos al cuadrado antes de sumarlos, obtenemos una cantidad que sí distingue entre rectas:

sum(proc_data$residuo^2)
[1] 4158.653

Ahora probemos distintos valores posibles de pendiente y calculemos, para cada uno, la suma de residuos al cuadrado:

x <- proc_data$edad
y <- proc_data$confianza_vecinos

pendientes <- seq(-0.01, 0.03, by = 0.001)

ssr <- sapply(pendientes, function(b1) {
  b0 <- mean(y) - b1 * mean(x)          # el intercepto que acompaña a cada pendiente
  sum((y - (b0 + b1 * x))^2)            # suma de residuos al cuadrado
})

resultados <- data.frame(pendiente = pendientes, ssr = ssr)

ggplot(resultados, aes(x = pendiente, y = ssr)) +
  geom_line(color = "#04245f", linewidth = 1) +
  geom_vline(xintercept = coef(modelo)[2], color = "#c0392b", linetype = "dashed") +
  labs(x = "Valor de la pendiente (b1)",
       y = "Suma de residuos al cuadrado") +
  theme_minimal()
Figura 5: Suma de residuos al cuadrado según distintos valores de la pendiente

La línea segmentada roja marca el valor de la pendiente estimada por lm(). Como se observa, coincide exactamente con el punto más bajo de la curva: ninguna otra recta produce una suma de residuos al cuadrado menor. Eso es, literalmente, lo que significa “mínimos cuadrados ordinarios”.

10 Reporte de resultados

Los resultados de un modelo de regresión se comunican en una tabla. La función tab_model de la librería sjPlot produce tablas con formato de publicación:

tab_model(modelo,
          show.ci = FALSE,
          p.style = "stars",
          dv.labels = c("Confianza en vecinos"),
          pred.labels = c("(Intercepto)", "Edad"),
          string.pred = "Predictores",
          string.est = "β")
  Confianza en vecinos
Predictores β
(Intercepto) 2.83 ***
Edad 0.01 ***
Observations 2906
R2 / R2 adjusted 0.010 / 0.010
* p<0.05   ** p<0.01   *** p<0.001

La tabla incluye columnas que aún no hemos revisado, como los valores p y el \(R^2\). Corresponden a la evaluación del ajuste y a la inferencia estadística, que son los contenidos de la próxima sesión teórica. Por ahora, concéntrese en la columna de coeficientes.

11 Tarea

11.1 Preparación

  • Cargar los paquetes y la base ELSOC, y filtrar por la ola 4 (2019)

  • Seleccionar las variables c01 (satisfacción con la democracia), m01 (educación), m0_sexo (sexo) y m0_edad (edad)

  • Recodificar los valores perdidos, renombrar las variables y quedarse con los casos completos

11.2 Medias condicionales

  • Calcular la media condicional de satisfacción con la democracia para cada nivel educacional, indicando el número de casos de cada uno

  • Elaborar un gráfico que muestre esas medias condicionales junto con la recta de regresión, y comentar en una o dos oraciones qué tan bien la recta resume la tendencia observada

11.3 Estimación manual

  • Estimar a mano los coeficientes \(b_0\) y \(b_1\) del modelo que utiliza educación como variable independiente y satisfacción con la democracia como dependiente

  • Reportar las sumas intermedias (productos cruzados y diferencias al cuadrado) que utilizó en el cálculo

11.4 Estimación con lm()

  • Estimar el mismo modelo con lm() y verificar que los coeficientes coinciden con los del punto anterior

  • Escribir la ecuación resultante

11.5 Interpretación y predicción

  • Interpretar la pendiente en términos sustantivos, en una o dos oraciones

  • Evaluar si el intercepto tiene una interpretación plausible en este caso. Si no la tiene, centrar la variable independiente y volver a interpretarlo

  • Calcular el valor predicho de satisfacción con la democracia para los niveles educacionales 3, 6 y 9

11.6 Residuos

  • Calcular los residuos del modelo y elaborar un gráfico de dispersión que muestre la recta estimada

  • Identificar el caso con el residuo más grande en valor absoluto y comentar brevemente qué significa

11.7 Reporte

  • Presentar una tabla de resultados con tab_model

  • Redactar un párrafo breve (máximo 250 palabras) que reporte el modelo estimado, interprete el coeficiente de interés y señale al menos una limitación de la conclusión obtenida