-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathRegresiones1.qmd
990 lines (816 loc) · 33.2 KB
/
Regresiones1.qmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
---
title: "Regresiones (parte 1)"
author: "Rodrigo Zepeda"
date: "`r Sys.Date()`"
format: html
editor: visual
output:
tufte::tufte_html:
tufte_features: ["fonts", "background","italics"]
toc: false
includes:
in_header: javascriptcall.html
css: style.css
tufte::tufte_handout:
citation_package: natbib
latex_engine: xelatex
tufte::tufte_book:
citation_package: natbib
latex_engine: xelatex
bibliography: skeleton.bib
link-citations: yes
editor_options:
chunk_output_type: console
---
## Paquetes y datos a utilizar
A lo largo de esta sección usaremos los siguientes paquetes:
```{r}
#| warning: false
#| output: false
library(tidyverse)
library(tidymodels)
library(ggfortify) #autoplot para diagnósticos
library(rstanarm) #para bayesiana
library(poissonreg) #modelo Poisson
#Siempre que uses tidymodels
tidymodels_prefer()
```
En general usaremos la filosofía `tidymodels` para combinar los resultados con el `tidyverse`. Si quieres saber más de `tidymodels` te recomiendo checar [su libro](https://www.tmwr.org) o [su página web](https://www.tidymodels.org/learn/).
### Embarazo adolescente y pobreza
Para los datos usaremos la información de [*Utts y Heckard*](https://online.stat.psu.edu/stat462/sites/onlinecourses.science.psu.edu.stat462/files/data/poverty/index.txt) para determinar si hay una relación entre embarazo adolescente y pobreza.
```{r}
emb_pob <- read_delim("https://online.stat.psu.edu/stat462/sites/onlinecourses.science.psu.edu.stat462/files/data/poverty/index.txt")
```
Las variables `Brth15to17` y `Brth18to19` son las tasas brutas de natalidad por cada 1000 mujeres (en el año 2002) en adolescentes de 15 a 17 años y de 18 a 19 respectivamente. La variable `PovPct` representa la proporción (%) de la población que vive bajo la línea de pobreza en cada una de las entidades de EEUU (`Location`). Las variables `ViolCrime` y `TeenBrth` no se explican por lo que no las usaremos.
# Regresiones lineales
Usaremos `Brth15to17` y `PovPct` para estudiar si hay una relación entre la tasa de natalidad en adolescentes y el porcentaje de la población en pobreza. Para ello comenzaremos con graficar:
```{r}
ggplot(emb_pob) +
geom_point(aes(x = PovPct, y = Brth15to17), color = "#bc5090", size = 3) +
labs(
x = "Porcentaje en pobreza",
y = "Tasa bruta de natalidad (por cada 1,000 mujeres adolescentes)",
title = "Relación entre tasa bruta de natalidad en adolescentes\nde 15 a 19 años y porcentaje en pobreza"
) +
theme_bw()
```
Parece que a mayor porcentaje de pobreza, mayor tasa bruta de natalidad en adolescentes.
## Planteamiento clásico
Si la relación fuera perfecta todos los casos caerían *exactamente* en una línea recta como sigue:
```{r}
#| echo: false
tibble(
PovPct = seq(0, 25, length.out = 51),
) %>%
mutate(Brth15to17 = 3*PovPct + 5) %>%
ggplot() +
geom_segment(aes(x = 10, xend = 15, y = 35, yend = 50)) +
geom_point(aes(x = PovPct, y = Brth15to17), color = "#bc5090", size = 3) +
geom_segment(aes(x = 10, xend = 15, y = 35, yend = 35)) +
geom_segment(aes(x = 15, xend = 15, y = 35, yend = 50)) +
annotate("label", x = 0, y = 30, label = "Intercepto = 5", hjust = 0) +
annotate("label", x = 12.5, y = 30, label = "Δ Pobreza = 5") +
annotate("label", x = 16, y = 40, label = "Δ Natalidad = 15", hjust = 0) +
annotate("label", x = 12.5, y = 45, label = "Pendiente = Δ Natalidad / Δ Pobreza = 3", hjust = 1) +
geom_segment(aes(x = 0, xend = 0, y = 29, yend = 6),
arrow = arrow(length = unit(0.2,"inches"))) +
geom_point(aes(x = 0, y = 5), color = "#ff6361", size = 3,
data = NULL) +
geom_point(aes(x = 10, y = 35), color = "#ff6361", size = 3,
data = NULL) +
geom_point(aes(x = 15, y = 50), color = "#ff6361", size = 3,
data = NULL) +
labs(
x = "Porcentaje en pobreza",
y = "Tasa bruta de natalidad (por cada 1,000 mujeres adolescentes)",
title = "Si el mundo fuera como el modelo de línea se vería así:"
) +
theme_bw()
```
donde es necesario especificar dos parámetros: el intercepto (el valor que toma cuando la $x$ en este caso `PovPct` vale cero) y la pendiente (el valor que relaciona por cada unidad de aumento en `PovPct` cuánto aumenta `Brth15to17`).
La ecuación de la línea está dada por:
$$
y = \beta_0 + \beta_1 x
$$
donde $\beta_0$ es el intercepto y $\beta_1$ la pendiente. Usando la terminología de arriba:
$$
\text{Brth15to17} = \text{Intercepto} + \text{Pendiente}\times \text{PovPct}
$$ en particular en ese ejemplo:
$$
\text{Brth15to17} = 5 + 3\cdot \text{PovPct}
$$
La idea es que el intercepto ($\beta_0$ ó $5$) te indica dónde comienza tu línea cuando no tienes $x$'s (es decir cuando $\text{PovPct} = 0$). El intercepto controla la altura de la línea como puedes ver en la siguiente gráfica donde puse varios interceptos distintos:
```{r}
#| echo: false
tibble(
x = 0:25,
) %>%
mutate(y1 = 5 + 3*x) %>%
mutate(y2 = 10 + 3*x) %>%
mutate(y3 = 0 + 3*x) %>%
mutate(y4 = -5 + 3*x) %>%
mutate(y5 = 15 + 3*x) %>%
pivot_longer(cols = y1:y5) %>%
mutate(intercepto = case_when(
name == "y1" ~ 5,
name == "y2" ~ 10,
name == "y3" ~ 0,
name == "y4" ~ -5,
name == "y5" ~ 15
)) %>%
ggplot() +
geom_line(aes(x = x, y = value, color = as.character(intercepto))) +
scale_color_manual("Interceptos",
values = c("#003f5c","#58508d","#bc5090","#ff6361","#ffa600")) +
labs(
x = "Porcentaje en pobreza",
y = "Tasa bruta de natalidad (por cada 1,000 mujeres adolescentes)",
title = "Diferentes interceptos:"
) +
theme_bw()
```
por otro lado la idea de la pendiente ($\beta_1$ ó $3$) es retratar cómo cambia la $y$ (en este caso $\text{Brth15to17}$) por cada unidad que cambia la $x$ (en este caso $\text{PovPct}$). El valor de $3$ por ejemplo indica que por cada aumento en 1 en $\text{PovPct}$ la variable $\text{Brth15to17}$ aumenta en $3$. Este cambio es proporcional; es decir si ahora $\text{PovPct}$ aumenta 4 (por decir algo) $\text{Brth15to17}$ aumenta $3\times 4 = 12$ unidades. La siguiente gráfica muestra varias líneas todas comenzando en el mismo intercepto de $5$:
```{r}
#| echo: false
tibble(
x = 0:25,
) %>%
mutate(y1 = 5 + 3*x) %>%
mutate(y2 = 5 + 6*x) %>%
mutate(y3 = 5 + 0*x) %>%
mutate(y4 = 5 + -3*x) %>%
mutate(y5 = 5 + -6*x) %>%
pivot_longer(cols = y1:y5) %>%
mutate(pendiente = case_when(
name == "y1" ~ 3,
name == "y2" ~ 6,
name == "y3" ~ 0,
name == "y4" ~ -3,
name == "y5" ~ -6
)) %>%
ggplot() +
geom_line(aes(x = x, y = value, color = as.character(pendiente))) +
scale_color_manual("Pendientes",
values = c("#003f5c","#58508d","#bc5090","#ff6361","#ffa600")) +
labs(
x = "Porcentaje en pobreza",
y = "Tasa bruta de natalidad (por cada 1,000 mujeres adolescentes)",
title = "Diferentes pendientes:"
) +
theme_bw()
```
Como ya vimos en la primer figura el mundo no es tan perfecto que todo sea una línea recta. Hay un poco de aleatoriedad involucrada (sea por variables no medidas, por errores de medición o porque el mundo no sea determinista). Por lo cual se plantea que en lugar de que la $y$ sea *exactamente* $\beta_0 + \beta_1 x$ planteamos que la $y$ proviene de una variable aleatoria normal donde la media de esa normal **es** $\beta_0 + \beta_1 x$; es decir:
$$
y \sim \textrm{Normal}( \beta_0 + \beta_1 x, \sigma^2)
$$
o dicho de otra manera:
$$
\text{Brth15to17} \sim \textrm{Normal}(\text{Intercepto} + \text{Pendiente}\times \text{PovPct}, \sigma^2)
$$
donde la $\sigma^2$ es la varianza de dicha normal. Puesto gráficamente lo que esto quiere decir es que si, por ejemplo, el intercepto es $5$ y la pendiente $3$ entonces cada medición de $y$ viene de una normal ligeramente distinta:
$$
\text{Brth15to17} \sim \textrm{Normal}(5 + 3\times \text{PovPct}, \sigma^2)
$$
```{r}
#| echo: false
library(ggridges)
x <- c(0, 5, 10, 15)
vals <- seq(-5, 5, length.out = 1000)
tibble(
y1 = rnorm(1000000, mean = 5 + 3*x[1], 4),
y2 = rnorm(1000000, mean = 5 + 3*x[2], 4),
y3 = rnorm(1000000, mean = 5 + 3*x[3], 4),
y4 = rnorm(1000000, mean = 5 + 3*x[4], 4),
) %>%
pivot_longer(cols = everything()) %>%
mutate(xvals = case_when(
name == "y1" ~ x[1],
name == "y2" ~ x[2],
name == "y3" ~ x[3],
name == "y4" ~ x[4]
)) %>%
ggplot() +
geom_density_ridges(aes(x = value, y = xvals, group = xvals),
bandwidth = 0.14, fill = "#003f5c",
alpha = 0.5, rel_min_height = 0.0001) +
coord_flip() +
labs(
y = "Porcentaje en pobreza",
x = "Tasa bruta de natalidad (por cada 1,000 mujeres adolescentes)",
title = "Las y's provienen de realizaciones de una normal con media 5 + 3x"
) +
theme_bw() +
geom_line(aes(y = x, x = 3*x + 5), data = tibble(x = c(0, 5, 10, 15)),
color = "#58508d", size = 1) +
geom_point(aes(y = x, x = 3*x + 5), data = tibble(x = c(0, 5, 10, 15)),
color = "#58508d", size = 3) +
geom_point(aes(y = x, x = 3*x + 5), data = tibble(x = c(0, 5, 10, 15)),
color = "white", size = 1) +
annotate("label", x = 20, y = 20,
label = "La idea es que para cada valor de x\nhay una normal con media 3x + 5")
```
Dicho de otra forma, suponemos que en un mundo perfecto los valores de $y$ (`Brth15to17`) estarían completamente determinados por los de $x$ (`PovPct`) mediante la ecuación de la recta. **Pero** como el mundo no es perfecto entonces la $y$ proviene de una normal con promedio dado por la recta. Los puntos (como puedes ver an la siguiente gráfica) se centran más en torno a los promedios de las normales sin embargo están colocados aleatoriamente pues corresponden a distintas realizaciones de $y$.
```{r}
#| echo: false
library(ggridges)
x <- c(0, 5, 10, 15)
vals <- seq(-5, 5, length.out = 1000)
tibble(
y1 = rnorm(1000000, mean = 5 + 3*x[1], 4),
y2 = rnorm(1000000, mean = 5 + 3*x[2], 4),
y3 = rnorm(1000000, mean = 5 + 3*x[3], 4),
y4 = rnorm(1000000, mean = 5 + 3*x[4], 4),
) %>%
pivot_longer(cols = everything()) %>%
mutate(xvals = case_when(
name == "y1" ~ x[1],
name == "y2" ~ x[2],
name == "y3" ~ x[3],
name == "y4" ~ x[4]
)) %>%
ggplot() +
geom_density_ridges(aes(x = value, y = xvals, group = xvals),
bandwidth = 0.14, fill = "#003f5c",
alpha = 0.5, rel_min_height = 0.0001) +
coord_flip() +
labs(
y = "Porcentaje en pobreza",
x = "Tasa bruta de natalidad (por cada 1,000 mujeres adolescentes)",
title = "Las y's provienen de realizaciones de una normal con media 5 + 3x"
) +
theme_bw() +
geom_line(aes(y = x, x = 3*x + 5), data = tibble(x = c(0, 5, 10, 15)),
color = "#58508d", size = 1) +
geom_point(aes(y = x, x = 3*x + 5), data = tibble(x = c(0, 5, 10, 15)),
color = "#58508d", size = 3) +
geom_point(aes(y = x, x = 3*x + 5), data = tibble(x = c(0, 5, 10, 15)),
color = "white", size = 1) +
geom_point(aes(y = x, x = y), color = "#bc5090", data = (tibble(
x = seq(0, 15, length.out = 100),
) %>%
mutate(y = rnorm(100, 3*x + 5, sd = 4)))) +
annotate("label", x = 20, y = 20,
label = "La idea es que para cada valor de x\nhay una normal con media 3x + 5")
```
Por ejemplo si el porcentaje de pobreza (`PovPct`) es $10$ entonces la normal de la que provienen estos datos es:
$$
\text{Brth15to17} \sim \textrm{Normal}(\underbrace{5 + 3\times 10}_{35}, \sigma^2)
$$
mientras que si el porcentaje en pobreza es $20$ entonces la normal es:
$$
\text{Brth15to17} \sim \textrm{Normal}(\underbrace{5 + 3\times 20}_{65}, \sigma^2)
$$
Por supuesto que no hay nada de especial con el modelo normal y alguien podría elegir otra distribución (por ejemplo una Gamma) y establecer que:
$$
\text{Brth15to17} \sim \textrm{Gamma}(\text{Intercepto} + \text{Pendiente}\times \text{PovPct}, \beta)
$$
Estos modelos son algunos de los lineales generalizados y los discutiremos más adelante. Por ahora nos quedaremos con la idea del modelo dado por:
$$
\text{Brth15to17} \sim \textrm{Normal}(\text{Intercepto} + \text{Pendiente}\times \text{PovPct}, \sigma^2)
$$
> **Nota** quizá conoces la regresión lineal bajo la idea clásica de que $$
> y = \beta_0 + \beta_1 x + \epsilon
> $$ donde $\epsilon\sim\text{Normal}(0,\sigma^2)$ son los errores normales. Esta definición es equivalente a la que damos aquí pues por [propiedades aditivas de la normal](https://en.wikipedia.org/wiki/Normal_distribution#Properties) $\epsilon + \beta_0 + \beta_1 x$ se sigue distribuyendo normal pero con la media ahora dada por lo agregado ($\beta_0 + \beta_1 x$). Las ventajas de esta notación es que un modelo para regresión Poisson es simplemente: $$
> y \sim \textrm{Poisson}(\beta_0 + \beta_1 x)
> $$ y un modelo para regresión logística es: $$
> y \sim \textrm{Bernoulli}\big(\textrm{logit}(\beta_0 + \beta_1 x)\big)
> $$
## Planteamiento en `R`
Lo que nos toca ahora es programar nuestro modelo para ello seguiremos la filosofía de tidymodels que pretende unificar bajo la misma notación todos los modelos. La notación básica es como sigue:
```{r}
#| eval: false
modelo %>%
set_engine("tipo de ajuste") %>%
fit("formula a ajustar", data = tus_datos)
```
A partir del ajuste se pueden predecir cosas con `predict`:
```{r}
#| eval: false
modelo %>%
set_engine("tipo de ajuste") %>%
fit("formula a ajustar", data = tus_datos) %>%
predict()
```
o extraer nuevos datos con `extract_fit_engine` y `tidy`:
```{r}
#| eval: false
modelo %>%
set_engine("tipo de ajuste") %>%
fit("formula a ajustar", data = tus_datos) %>%
extract_fit_engine() %>%
tidy()
```
Comencemos con nuestro primer modelo: una regresión lineal clásica dada por:
$$
\text{Brth15to17} \sim \textrm{Normal}(\text{Intercepto} + \text{Pendiente}\times \text{PovPct}, \sigma^2)
$$
En `R` el `engine` que necesitamos es "lm" que es el clásico:
```{r}
#Ajusta un modelo lineal
#Brth15to17 = intercepto + pendiente*PovPct
modelo_ajustado <- linear_reg() %>%
set_engine("lm") %>%
fit(Brth15to17 ~ PovPct, data = emb_pob) #Notación y ~ x
```
Nota que a diferencia de `Stata`, `R` no arroja demasiados resultados. Podemos usar `extract_fit_engine` combinado con `summary` para obtenerlos:
```{r}
#Ajusta un modelo lineal
#Brth15to17 = intercepto + pendiente*PovPct
modelo_ajustado %>%
extract_fit_engine() %>%
summary()
```
o bien con `tidy` si deseamos nos devuelva una tabla de resultados:
```{r}
#Ajusta un modelo lineal
#Brth15to17 = intercepto + pendiente*PovPct
modelo_ajustado %>%
extract_fit_engine() %>%
tidy()
```
Según el tipo de regresión que estemos haciendo es el tipo de tabla que regresa `tidy` (ver `?tidy`). En particular, por ejemplo, podemos modificar para que devuelva intervalos de confianza al 90%:
```{r}
#Ajusta un modelo lineal
#Brth15to17 = intercepto + pendiente*PovPct
modelo_ajustado %>%
tidy(conf.int = T, conf.level = 0.90)
```
En este caso, el modelo estima que el intercepto ($\beta_0$) es $4.27$ y la pendiente ($\beta_1$) es $1.37$. Como son *estimadores* del verdadero valor se denotan con *gorrito*: $\hat\beta_0 = 4.27$ y $\hat\beta_1 = 1.37$.
Podemos utilizar la función de `predict` para que el modelo nos muestre cómo cree que son los verdaderos valores en relación a los ajustados:
```{r}
#Ajusta un modelo lineal
#Brth15to17 = intercepto + pendiente*PovPct
predichos <- modelo_ajustado %>%
predict(new_data = emb_pob)
intervalo_predichos <- modelo_ajustado %>%
predict(new_data = emb_pob, type = "pred_int", level = 0.95)
#Juntamos los predichos con los observados
obs_y_modelo <- emb_pob %>%
cbind(predichos) %>%
cbind(intervalo_predichos)
#Graficamos
ggplot(obs_y_modelo) +
geom_ribbon(aes(x = PovPct, ymin = .pred_lower, ymax = .pred_upper),
fill = "#003f5c", size = 1, alpha = 0.5) +
geom_point(aes(x = PovPct, y = Brth15to17), color = "#bc5090", size = 3) +
geom_line(aes(x = PovPct, y = .pred),
color = "#003f5c", size = 1) +
labs(
x = "Porcentaje en pobreza",
y = "Tasa bruta de natalidad (por cada 1,000 mujeres adolescentes)",
title = "Relación entre tasa bruta de natalidad en adolescentes\nde 15 a 19 años y porcentaje en pobreza"
) +
theme_bw()
```
Nada más a ojo no parece que el modelo sea el mejor pues puedes ver que no explica bien la variabilidad (los observados varían mucho respecto al intervalo). La $R^2$, una métrica que explica cuánto de la varianza captura el modelo tampoco es muy buena:
```{r}
resumen_ajuste <- modelo_ajustado %>%
extract_fit_engine() %>%
summary()
#R^2 clásica
resumen_ajuste$r.squared
#R^2 ajustada
resumen_ajuste$adj.r.squared
```
Podemos checar las diferentes gráficas de diagnóstico:
```{r}
library(ggfortify)
autoplot(modelo_ajustado, which = 1:5)
```
Veamos qué significa cada una de ellas y juguemos un poco con `R` para irlas modificando.
### Residuales contra ajustados
Los **residuales** son la diferencia entre el modelo ($\hat{y}$) y lo real $y$. En el caso que estábamos trabajando tenemos que con nuestro modelo podemos predecir los valores de `Brth15to17` a partir del porcentaje en pobreza `PovPct`. A los valores predichos por el modelo de `Brth15to17` les ponemos un gorro encima y los llamamos: $\widehat{\text{Brth15to17}}$. Estos están dados por la siguiente función:
$$
\widehat{\text{Brth15to17}} = 4.26 + 1.37 \cdot \text{PovPct}
$$
por ejemplo para el porcentaje en pobreza de $20.1$ obtendríamos:
$$
\widehat{\text{Brth15to17}} = 4.26 + 1.37 \cdot \text{PovPct} = 31.797
$$
por otro lado el verdadero valor de cuando el `PovPct` es $20.1$ (estado de `Alabama`) es $\text{Brth15to17} = 31.5$. La diferencia entre el verdadero valor ($\text{Brth15to17} = 31.5$) y el predicho por el modelo ($\widehat{\text{Brth15to17}} = 31.797$) se conoce como el residual. La idea es que en un modelo bueno no debe haber patrones en los residuales (todos deben de flotar en torno al cero pero no mostrar un patrón).
Veamoslo en nuestra base:
```{r}
obs_y_modelo <- obs_y_modelo %>%
mutate(residuales = Brth15to17 - .pred)
ggplot(obs_y_modelo) +
geom_point(aes(x = .pred, y = residuales), color = "#ff6361") +
labs(
x = "Valores ajustados (predichos)",
y = "Residuales",
title = "Residuales vs ajustados"
) +
theme_bw() +
geom_hline(aes(yintercept = 0), linetype = "dashed")
```
En esta gráfica el modelo predice mejor rumbo al final que en medio y esto parece estar corroborado por la gráfica del modelo (previa). Nada más para darnos una idea veamos una gráfica de *malos* residuales y una de *buenos*
```{r}
#| echo: false
library(cowplot)
datos_bien <- tibble(
x = seq(0, 25, length.out = 100)
) %>%
mutate(y = rnorm(n(), mean = 3*x + 5, 4))
modelo_1 <- linear_reg() %>%
set_engine("lm") %>%
fit(y ~ x, data = datos_bien)
datos_bien <- datos_bien %>%
cbind(
predict(modelo_1, new_data = datos_bien)
) %>%
cbind(
predict(modelo_1, new_data = datos_bien, type = "conf_int", level = 0.9)
) %>%
mutate(residuales = y - .pred)
modelo_bien <- ggplot(datos_bien) +
geom_point(aes(x = x, y = y), color = "#ff6361") +
geom_line(aes(x = x, y = .pred), color = "#003f5c") +
labs(
x = "x",
y = "y",
title = "Un buen modelo"
) +
theme_bw()
plot_bien <- ggplot(datos_bien) +
geom_point(aes(x = .pred, y = residuales), color = "#ff6361") +
labs(
x = "Valores ajustados (predichos)",
y = "Residuales",
title = "En un buen modelo no se observa\nningún patrón"
) +
theme_bw() +
geom_hline(aes(yintercept = 0), linetype = "dashed")
datos_mal <- tibble(
x = seq(0, 25, length.out = 100)
) %>%
mutate(y = rnorm(n(), mean = 2*x^2 + 3*x + 5, 4))
modelo_2 <- linear_reg() %>%
set_engine("lm") %>%
fit(y ~ x, data = datos_mal)
datos_mal <- datos_mal %>%
cbind(
predict(modelo_2, new_data = datos_mal)
) %>%
cbind(
predict(modelo_2, new_data = datos_mal, type = "conf_int", level = 0.9)
) %>%
mutate(residuales = y - .pred)
modelo_mal <- ggplot(datos_mal) +
geom_point(aes(x = x, y = y), color = "#ffa600") +
geom_line(aes(x = x, y = .pred), color = "#003f5c") +
labs(
x = "x",
y = "y",
title = "Un mal modelo"
) +
theme_bw()
plot_mal <- ggplot(datos_mal) +
geom_point(aes(x = .pred, y = residuales), color = "#ffa600") +
labs(
x = "Valores ajustados (predichos)",
y = "Residuales",
title = "En un mal modelo se observa\nun patrón"
) +
theme_bw() +
geom_hline(aes(yintercept = 0), linetype = "dashed")
plot_grid(modelo_bien, modelo_mal, plot_bien, plot_mal, ncol = 2)
```
### Escala locación
Representa la escala locación contra los residuales estandarizados. La idea de la gráfica es ver que la varianza $\sigma^2$ del modelo no cambie conforme cambia la $x$ (propiedad de *homoscedasticidad*). Para ello graficamos los residuales estandarizados dados por los residuales mismos dividos entre su desviación estándar:
$$
r_{\text{Std}} = \frac{\hat{y} - y}{\text{sd}(\hat{y} - y)} = \frac{\text{Residuales}}{\text{sd}\big(\text{Residuales}\big)}
$$
estos residuales estandarizados los podemos calcular en `R` como sigue:
```{r}
obs_y_modelo <- obs_y_modelo %>%
mutate(residuales_std = residuales/sd(residuales))
```
Si los graficamos contra los valores ajustados no deberíamos de ver ningún patrón:
```{r}
ggplot(obs_y_modelo) +
geom_point(aes(x = .pred, y = residuales_std), color = "#ff6361") +
labs(
x = "Valores ajustados (predichos)",
y = "Residuales estandarizados",
title = "Escala Locación"
) +
theme_bw() +
geom_hline(aes(yintercept = 0), linetype = "dashed")
```
Podemos ver cómo se ven estos puntos en el modelo ideal vs en un modelo donde la $\sigma^2$ depende de la $x$:
```{r}
#| echo: false
datos_bien <- tibble(
x = seq(0, 25, length.out = 100)
) %>%
mutate(y = rnorm(n(), mean = 3*x + 5, 4))
modelo_1 <- linear_reg() %>%
set_engine("lm") %>%
fit(y ~ x, data = datos_bien)
datos_bien <- datos_bien %>%
cbind(
predict(modelo_1, new_data = datos_bien)
) %>%
cbind(
predict(modelo_1, new_data = datos_bien, type = "conf_int", level = 0.9)
) %>%
mutate(residuales = y - .pred) %>%
mutate(residuales_std = residuales/sd(residuales))
modelo_bien <- ggplot(datos_bien) +
geom_point(aes(x = x, y = y), color = "#ff6361") +
geom_line(aes(x = x, y = .pred), color = "#003f5c") +
labs(
x = "x",
y = "y",
title = "Un buen modelo"
) +
theme_bw()
plot_bien <- ggplot(datos_bien) +
geom_point(aes(x = .pred, y = residuales_std), color = "#ff6361") +
labs(
x = "Valores ajustados (predichos)",
y = "Residuales estandarizados",
title = "Modelo bien (sin patrón)"
) +
theme_bw() +
geom_hline(aes(yintercept = 0), linetype = "dashed")
datos_mal <- tibble(
x = seq(0, 25, length.out = 100)
) %>%
mutate(y = rnorm(n(), mean = 3*x + 5, x^2))
modelo_2 <- linear_reg() %>%
set_engine("lm") %>%
fit(y ~ x, data = datos_mal)
datos_mal <- datos_mal %>%
cbind(
predict(modelo_2, new_data = datos_mal)
) %>%
cbind(
predict(modelo_2, new_data = datos_mal, type = "conf_int", level = 0.9)
) %>%
mutate(residuales = y - .pred) %>%
mutate(residuales_std = residuales/sd(residuales))
modelo_mal <- ggplot(datos_mal) +
geom_point(aes(x = x, y = y), color = "#ffa600") +
geom_line(aes(x = x, y = .pred), color = "#003f5c") +
labs(
x = "x",
y = "y",
title = "Un mal modelo"
) +
theme_bw()
plot_mal <- ggplot(datos_mal) +
geom_point(aes(x = .pred, y = residuales_std), color = "#ffa600") +
labs(
x = "Valores ajustados (predichos)",
y = "Residuales estandarizados",
title = "Modelo mal (con patrón)"
) +
theme_bw() +
geom_hline(aes(yintercept = 0), linetype = "dashed")
plot_grid(modelo_bien, modelo_mal, plot_bien, plot_mal, ncol = 2)
```
### Normal cuantil cuantil
La segunda gráfica corresponde a una gráfica cuantil cuantil. Esta la utilizamos para verificar la hipótesis de normalidad. En una gráfica cuantil cuantil se grafican los cuantiles de los residuales contra los cuantiles teóricos de la normal. Por ejemplo si la hacemos con sólo 4 puntos se vería algo así:
```{r}
#| echo: false
ggplot(tibble(x = c(-2, -1, 0, 1, 2),
y = c(-1, 0, 0.1, 0.4, 1))) +
geom_point(aes(x = x, y = y)) +
theme_bw() +
annotate("text", x = 0, y = 0,
label = "Cada punto es el cuantil q1 de la normal\ncontra el cuantil q1 de los residuales") +
labs(
x = "Cuantiles teóricos de la normal",
y = "Cuantiles observados de los residuales",
title = "Gráfica qq"
)
```
Podemos armar una gráfica cuantil cuantil con `ggplot2`:
```{r}
ggplot(obs_y_modelo, aes(sample = residuales)) +
stat_qq(color = "#ff6361") +
stat_qq_line(color = "#58508d") +
theme_bw() +
labs(
x = "Cuantiles teóricos de la normal",
y = "Cuantiles observados de los residuales",
title = "Gráfica qq"
)
```
La idea de la gráfica cuantil cuantil es que los puntos sigan la línea lo más posible. Veamos cómo se ve con los datos bien (y los mal)
```{r}
#| echo: false
qqmal <- ggplot(datos_mal, aes(sample = residuales)) +
stat_qq(color = "#ffa600") +
stat_qq_line(color = "#58508d") +
theme_bw() +
labs(
x = "Cuantiles teóricos de la normal",
y = "Cuantiles observados de los residuales",
title = "QQPLOT mal"
)
qqbien <- ggplot(datos_bien, aes(sample = residuales)) +
stat_qq(color = "#ff6361") +
stat_qq_line(color = "#58508d") +
theme_bw() +
labs(
x = "Cuantiles teóricos de la normal",
y = "Cuantiles observados de los residuales",
title = "QQPLOT bien"
)
plot_grid(modelo_bien, modelo_mal, qqbien, qqmal, ncol = 2)
```
### Residuales contra apalancamiento
El apalancamiento representa qué tanto cambia el modelo al quitar una sola observación. Para poner un ejemplo considera los siguientes datos donde hay un valor atípico y selecciono dos puntos de interés en dos colores:
```{r}
#| echo: false
datos_bien <- tibble(
x = seq(0, 25, length.out = 100)
) %>%
mutate(y = rnorm(n(), mean = 3*x + 5, 4))
datos_bien[50,"y"] <- 100 #outlier
datos_bien[50,"x"] <- 100 #outlier
ggplot(datos_bien) +
geom_point(aes(x = x, y = y), color = "#003f5c", size = 2) +
geom_segment(aes(x = 12, xend = as.numeric(datos_bien[50,"x"]),
y = 76, yend = as.numeric(datos_bien[50,"y"])),
color = "#bc5090",
arrow = arrow(length = unit(0.2,"inches"))) +
geom_segment(aes(x = 3, xend = as.numeric(datos_bien[12,"x"]),
y = 40, yend = as.numeric(datos_bien[12,"y"])),
color = "#bc5090",
arrow = arrow(length = unit(0.2,"inches"))) +
annotate("label", x = 3, y = 40, label = "Normal") +
annotate("label", x = 12, y = 75, label = "Influyente") +
geom_point(aes(x = x, y = y), data = datos_bien[50,], size = 2, color = "#ff6361") +
geom_point(aes(x = x, y = y), data = datos_bien[12,], size = 2, color = "#ffa600") +
theme_bw()
```
Veamos cómo cambia la regresión si dejo todos los puntos, si quito el normal y si quito el influyente:
```{r}
#| echo: false
ggplot(datos_bien) +
geom_point(aes(x = x, y = y), color = "#003f5c", size = 2) +
geom_smooth(aes(x = x, y = y, color = "Sin el normal"), method = "lm",
formula = y ~ x, se = F, data = datos_bien[-12,]) +
geom_smooth(aes(x = x, y = y, color = "Sin el influyente"), method = "lm",
formula = y ~ x, se = F, data = datos_bien[-50,]) +
geom_smooth(aes(x = x, y = y, color = "Todos los datos"), method = "lm",
formula = y ~ x, se = F) +
theme_bw() +
scale_color_manual("Modelo", values = c("#bc5090","#ff6361","#ffa600"))
```
Nota que el modelo no cambia prácticamente nada cuando hago la regresión sin el dato que marqué como `normal` pero cambia mucho cuando quito el que marqué como `influyente`. La gráfica de residuales contra apalancamiento muestra también el valor extraño:
```{r}
#| echo: false
modelo_res <- linear_reg() %>%
set_engine("lm") %>%
fit(y ~ x, data = datos_bien)
residuales <- modelo_res %>%
extract_fit_engine() %>%
residuals()
apalancamiento <- modelo_res %>%
extract_fit_engine() %>%
hatvalues()
ggplot() +
geom_point(aes(x = apalancamiento, y = residuales), color = "#ff6361") +
labs(
x = "Apalancamiento",
y = "Residuales",
title = "Residuales vs ajustados"
) +
theme_bw() +
geom_hline(aes(yintercept = 0), linetype = "dashed") +
annotate("label", x = 0.5, y = -80, label = "El influyente")
```
El apalancamiento mide la influencia de un dato y en `R` se puede calcular con `hatvalues`. Los datos con mayor **apalancamiento** siempre valen la pena checarlos para verificar que todo opera en orden.
```{r}
apalancamiento <- modelo_ajustado %>%
extract_fit_engine() %>%
hatvalues()
obs_y_modelo <- obs_y_modelo %>%
cbind(apalancamiento)
#Graficamos residuales contra apalancamiento
ggplot(obs_y_modelo) +
geom_point(aes(x = apalancamiento, y = residuales), color = "#ff6361") +
labs(
x = "Apalancamiento",
y = "Residuales",
title = "Residuales vs ajustados"
) +
theme_bw() +
geom_hline(aes(yintercept = 0), linetype = "dashed")
```
### Distancia de Cook
La distancia de Cook es un concepto similar al apalancamiento que identifica observaciones influyentes. Aquellos valores con distancia de Cook alta vale la pena revisar. En `R` podemos usar `cooks.distance` para calcular la distancia de Cook.
```{r}
distanciaCook <- modelo_ajustado %>%
extract_fit_engine() %>%
cooks.distance()
obs_y_modelo <- obs_y_modelo %>%
cbind(distanciaCook)
#Graficamos residuales contra apalancamiento
ggplot(obs_y_modelo) +
geom_col(aes(x = 1:nrow(obs_y_modelo), y = distanciaCook), fill = "#ff6361") +
labs(
x = "Observación (número de entrada en la base)",
y = "Distancia de Cook",
title = "Distancia de Cook"
) +
theme_bw()
```
podemos ver que en el ejemplo anterior (el de la observación influyente) la distancia de Cook es exagerada, tan exagerada que ni se alcanzan a ver los otros:
```{r}
#| echo: false
cookdist <- modelo_res %>%
extract_fit_engine() %>%
cooks.distance()
ggplot() +
geom_col(aes(x = 1:100, y = cookdist), fill = "#ff6361") +
labs(
x = "Observación",
y = "Distancia de Cook",
title = "Cook"
) +
theme_bw() +
annotate("label", x = 50, y = 40, label = "El influyente")
```
## Ejercicio
1. Corre el siguiente código para generar una base de datos de nombre `datLong` que contiene $4$ grupos. Para cada grupo genere una regresión lineal de la forma:
$$
y = \beta_0 + \beta_1 x
$$
Identifica cuáles regresiones sí ajustan bien y cuáles no mediante los gráficos de diagnóstico. Finalmente grafica tus datos $x$ contra $y$ y la regresión para ver que lo hayas hecho bien. ¿Hay alguna forma de corregir alguna de las que no ajusta bien?
```{r}
dat <- datasets::anscombe
datLong <- data.frame(
grupo = rep(1:4, each = 11),
x = unlist(dat[,c(1:4)]),
y = unlist(dat[,c(5:8)])
)
rownames(datLong) <- NULL
```
2. Lee la base de datos de $n = 345$ niños entre $6$ y $10$ años de Kahn, Michael (2005). Las variables de interés son $y = \text{FEV}$ el volumen de expiración forzada y $x = \text{edad}$ en años. Realiza una regresión lineal. Justifica que no se cumple la homocedasticidad mediante una gráfica de escala locación.
3. Plantee una regresión lineal usando [los datos de esta liga](https://data.princeton.edu/wws509/datasets/#salary) para determinar si el sexo influye en el salario. **Ojo** en la regresión es necesario incluir otras covariables.
## ¿Y si lo hacemos bayesiano?
Para hacer la misma regresión lineal pero con estadística bayesiana podemos nada más cambiar el `engine`:
```{r}
#Ajusta un modelo lineal
#Brth15to17 = intercepto + pendiente*PovPct
modelo_bayesiano <- linear_reg() %>%
set_engine("stan") %>%
fit(Brth15to17 ~ PovPct, data = emb_pob) #Notación y ~ x
```
Y podemos ver el ajuste:
```{r}
modelo_bayesiano %>%
extract_fit_engine() %>%
summary()
```
o realizar predicciones:
```{r}
predichos <- modelo_bayesiano %>%
extract_fit_engine() %>%
predict(new_data = emb_pob)
ic <- modelo_bayesiano %>%
extract_fit_engine() %>%
predictive_interval(newdata = emb_pob, prob = 0.95) #Para bayesiana
#Juntamos los predichos con los observados
obs_y_modelo <- emb_pob %>%
cbind(predichos) %>%
cbind(ic)
#Graficamos
ggplot(obs_y_modelo) +
geom_ribbon(aes(x = PovPct, ymin = `2.5%`, ymax = `97.5%`),
fill = "#003f5c", size = 1, alpha = 0.5) +
geom_point(aes(x = PovPct, y = Brth15to17), color = "#ffa600", size = 3) +
geom_line(aes(x = PovPct, y = predichos),
color = "#003f5c", size = 1) +
labs(
x = "Porcentaje en pobreza",
y = "Tasa bruta de natalidad (por cada 1,000 mujeres adolescentes)",
title = "Relación entre tasa bruta de natalidad en adolescentes\nde 15 a 19 años y porcentaje en pobreza"
) +
theme_bw()
```
Para validación del modelo puedes checar [esta página](https://mc-stan.org/rstanarm/articles/rstanarm.html#step-3-criticize-the-model-1) que explica `loo` ([ver paper](https://arxiv.org/abs/1507.04544):
```{r}
modelo_bayesiano %>%
extract_fit_engine() %>%
loo()
```
asi como su visualización (si el modelo no fuera bueno)
```{r}
modelo_bayesiano %>%
extract_fit_engine() %>%
loo() %>%
plot(label_points = TRUE)
```
## Ejercicio
1. Utilice las opciones de `engine` para cambiar el `prior_intercept` del intercepto a una `t` de Student y el `prior` de los coeficientes a una `Laplace`. ¿Cambia mucho el resultado?
# Continuación
[Ve a regresiones parte 2](https://rodrigozepeda.github.io/CursoR/Regresiones_2.html)
# Sistema
```{r}
sessioninfo::session_info()
```