library(tidyverse)
library(glue)
library(furrr)
plan(multiprocess)
set.seed(42)
Generamos las combinaciones de distribución del error y tamaño muestral \(n\) que se piden. Son 45 escenarios posibles y cada uno será repetido 1000 veces, lo que da una tabla de 45.000 filas:
datos <- cross_df(list(
n_obs = c(10, 25, 100, 250, 500, 1000, 1500, 2000, 3000),
tipo_de_error = c("exponencial", "lognormal", "uniforme", "chi_cuadrado", "t_student"),
run_id = 1:1000
))
nrow(datos)
## [1] 45000
Para cada uno de esos 45 x 1.000 escenarios, generamos las observaciones correspondientes:
generar_observaciones <- function(n_obs, tipo_de_error) {
observaciones <- tibble(
i = 1:n_obs,
x1 = runif(n_obs, -5, 5),
x2 = runif(n_obs, -5, 5),
x3 = runif(n_obs, -5, 5),
x4 = runif(n_obs, -5, 5)
)
if (tipo_de_error == "exponencial") {
errores <- rexp(n_obs, 0.5) - 2
} else if (tipo_de_error == "lognormal") {
errores <- rlnorm(n_obs, 0, 1) - exp(-0.5)
} else if (tipo_de_error == "uniforme") {
errores <- runif(n_obs, -3, 3)
} else if (tipo_de_error == "chi_cuadrado") {
errores <- rchisq(n_obs, df = 3) - 3
} else if (tipo_de_error == "t_student") {
errores <- rt(n_obs, df = 3)
} else {
stop(glue("No sé generar el tipo de error {tipo_de_error}"))
}
observaciones <- mutate(observaciones,
err = errores,
y = 4 + 2 * x1 - 3 * x2 + 0.5 * x3 + err)
return (observaciones)
}
datos <- mutate(datos, observaciones = future_map2(n_obs, tipo_de_error,
generar_observaciones))
Ajustamos el modelo para cada grupo de observaciones:
ajustar_modelo <- function(observaciones) {
return (lm(y ~ x1 + x2 + x3 + x4, data = observaciones))
}
datos <- mutate(datos, modelo = future_map(observaciones, ajustar_modelo))
Construimos en cada caso los IC de nivel 0.90 y de nivel 0.90 asintótico para β1 y β4, a partir del elemento “modelo” que produce lm
:
calcular_IC <- function(modelo, j, tipo = "exacto", alpha = 0.1) {
j <- j + 1 # La matriz empieza en beta 0, el beta_1 está en el índice 2, etc.
X <- model.matrix(modelo)
n_obs <- nrow(X)
p <- modelo$rank
beta_j_estimado <- modelo$coefficients[[j]]
desvio_del_beta_j <- sqrt(vcov(modelo)[[j, j]])
if (tipo == "exacto") {
cuantil <- qt(1 - alpha/2, n_obs - p)
} else if (tipo == "asintotico") {
cuantil <- qnorm(1 - alpha/2)
} else {
stop(glue("No conozco el tipo de IC '{tipo}'"))
}
delta <- cuantil * desvio_del_beta_j
return(c(beta_j_estimado - delta, beta_j_estimado + delta))
}
datos <-
datos %>%
mutate(
IC_1 = map(modelo, ~ calcular_IC(.x, j = 1)),
lower_1 = map_dbl(IC_1, ~ .x[[1]]),
upper_1 = map_dbl(IC_1, ~ .x[[2]]),
IC_4 = map(modelo, ~ calcular_IC(.x, j = 4)),
lower_4 = map_dbl(IC_4, ~ .x[[1]]),
upper_4 = map_dbl(IC_4, ~ .x[[2]]),
IC_1_asnt = map(modelo, ~ calcular_IC(.x, j = 1, tipo = "asintotico")),
lower_1_asnt = map_dbl(IC_1_asnt, ~ .x[[1]]),
upper_1_asnt = map_dbl(IC_1_asnt, ~ .x[[2]]),
IC_4_asnt = map(modelo, ~ calcular_IC(.x, j = 4, tipo = "asintotico")),
lower_4_asnt = map_dbl(IC_4_asnt, ~ .x[[1]]),
upper_4_asnt = map_dbl(IC_4_asnt, ~ .x[[2]])
) %>%
select(-IC_1, -IC_4, -IC_1_asnt, -IC_4_asnt)
Chequeamos para cada escenario si el β1 y el β4 estimados están contenidos en sus IC y en sus IC asintóticos:
BETA_1_VERDADERO <- 2
BETA_4_VERDADERO <- 0
datos <- mutate(datos,
beta_1_en_IC = (BETA_1_VERDADERO >= lower_1 & BETA_1_VERDADERO <= upper_1),
beta_4_en_IC = (BETA_4_VERDADERO >= lower_4 & BETA_4_VERDADERO <= upper_4),
beta_1_en_IC_asnt = (BETA_1_VERDADERO >= lower_1_asnt & BETA_1_VERDADERO <= upper_1_asnt),
beta_4_en_IC_asnt = (BETA_4_VERDADERO >= lower_4_asnt & BETA_4_VERDADERO <= upper_4_asnt))
Extraemos los betas estimados que nos interesan a columnas separadas:
datos <- mutate(datos,
betas_estimados = map(modelo, ~ .x$coefficients),
beta_1_est = map_dbl(betas_estimados, ~ .x[[2]]),
beta_2_est = map_dbl(betas_estimados, ~ .x[[3]]),
beta_4_est = map_dbl(betas_estimados, ~ .x[[5]]))
Para un solo caso por separado, en cada escenario, vemos si:
Nos quedamos con una sola iteración de cada escenario:
resumen_1_caso <-
datos %>%
filter(run_id == 1) %>%
select(matches("n_obs|tipo_de_error|lower|upper|beta_(1|4)")) %>%
gather(key = "name", value = "beta_value", -n_obs, -tipo_de_error) %>%
mutate(tipo_de_intervalo = ifelse(str_detect(name, "asnt"), "asintótico", "no asintótico"),
tipo_de_error = map_chr(tipo_de_error, ~ glue("error {.x}")))
Graficamos los IC en relación a los parámetros verdaderos:
resumen_1_caso %>%
filter(!str_detect(name, "beta_._en_IC")) %>%
ggplot() +
facet_grid(tipo_de_error ~ .) +
aes(y = as.factor(n_obs), x = beta_value) +
geom_point(
data = filter(resumen_1_caso, str_detect(name, "lower|upper")),
mapping = aes(shape = str_detect(name, "lower"),
color = tipo_de_intervalo,
size = tipo_de_intervalo),
size = 4,
) +
geom_point(
data = filter(resumen_1_caso, str_detect(name, "beta_._est")),
shape = 4
) +
geom_vline(xintercept = BETA_1_VERDADERO, size = .3, alpha = .5) +
geom_vline(xintercept = BETA_4_VERDADERO, size = .3, alpha = .5) +
scale_shape_manual(values = c(93, 91)) +
guides(shape = F) +
labs(
title = "Intervalos de Confianza para β1 y β4",
subtitle = glue("β1 verdadero = {BETA_1_VERDADERO}; β4 verdadero = {BETA_4_VERDADERO}"),
x = "β",
y = "n",
color = "Tipo de IC:"
) +
theme(legend.position = "top") +
xlim(-1, 3)
## Warning: Removed 7 rows containing missing values (geom_point).
library(gt)
resumen_1_caso %>%
filter(str_detect(name, "beta_._en_IC")) %>%
rename(
Contenido = beta_value,
n = n_obs,
`Tipo de IC` = tipo_de_intervalo,
`Parámetro` = name
) %>%
mutate(
Contenido = ifelse(Contenido, "✓", "☹"),
`Parámetro` = ifelse(str_detect(`Parámetro`, "beta_1"), "β1", "β4")
) %>%
spread(n, Contenido) %>%
group_by(tipo_de_error) %>%
gt %>%
tab_spanner("n", columns = matches("[1-9]+")) %>%
tab_source_note(
source_note = "✓: Parámetro contenido en el intervalo | ☹: Parámetro no contenido en el intervalo"
)
Parámetro | Tipo de IC | n | |||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
10 | 25 | 100 | 250 | 500 | 1000 | 1500 | 2000 | 3000 | |||
error chi_cuadrado | |||||||||||
β1 | asintótico | ✓ | ✓ | ☹ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | |
β1 | no asintótico | ✓ | ✓ | ☹ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | |
β4 | asintótico | ✓ | ☹ | ✓ | ✓ | ✓ | ☹ | ✓ | ✓ | ☹ | |
β4 | no asintótico | ✓ | ☹ | ✓ | ✓ | ✓ | ☹ | ✓ | ✓ | ☹ | |
error exponencial | |||||||||||
β1 | asintótico | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | |
β1 | no asintótico | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | |
β4 | asintótico | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | |
β4 | no asintótico | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | |
error lognormal | |||||||||||
β1 | asintótico | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ☹ | ✓ | |
β1 | no asintótico | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ☹ | ✓ | |
β4 | asintótico | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | |
β4 | no asintótico | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | |
error t_student | |||||||||||
β1 | asintótico | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ☹ | ✓ | |
β1 | no asintótico | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ☹ | ✓ | |
β4 | asintótico | ✓ | ☹ | ✓ | ✓ | ✓ | ✓ | ☹ | ✓ | ✓ | |
β4 | no asintótico | ✓ | ☹ | ✓ | ✓ | ✓ | ✓ | ☹ | ✓ | ✓ | |
error uniforme | |||||||||||
β1 | asintótico | ✓ | ☹ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | |
β1 | no asintótico | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | |
β4 | asintótico | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | |
β4 | no asintótico | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | |
✓: Parámetro contenido en el intervalo | ☹: Parámetro no contenido en el intervalo |
Para cada tipo de escenario, repetimos 1000 veces y analizamos:
analisis_IC <-
datos %>%
# filter(n_obs %in% c(10, 25, 100)) %>%
group_by(n_obs, tipo_de_error) %>%
summarise(
`β1` = mean(beta_1_en_IC),
`β4` = mean(beta_4_en_IC),
`β1 asnt` = mean(beta_1_en_IC_asnt),
`β4 asnt` = mean(beta_4_en_IC_asnt)
) %>%
ungroup %>%
rename(
Modelo = tipo_de_error,
n = n_obs
) %>%
mutate(Modelo = map_chr(Modelo, ~ glue("Error {.x}")))
analisis_IC %>%
group_by(n) %>%
group_by(Modelo) %>%
gt(
rowname_col = "tipo_de_error",
groupname_col = ""
) %>%
tab_header(title = "Proporción de IC que contienen a los parámetros según n y tipo de error") %>%
tab_spanner(label = "IC 90%", columns = vars(`β1`, `β4`)) %>%
tab_spanner(label = "IC 90% asintótico", columns = vars(`β1 asnt`, `β4 asnt`)) %>%
data_color(
columns = matches("β"),
colors = scales::col_bin(palette = c("Tomato", "Black"), bins = c(0, 0.89, 1)),
apply_to = "text"
) %>%
cols_align("center") %>%
tab_footnote(
footnote = "En rojo los escenarios donde menos del 89% de los intervalos contuvo al parámetro.",
locations = cells_column_labels(columns = matches("β"))
) %>%
tab_options(footnote.glyph = c("*"))
Proporción de IC que contienen a los parámetros según n y tipo de error | ||||
---|---|---|---|---|
n | IC 90% | IC 90% asintótico | ||
β1* | β4* | β1 asnt* | β4 asnt* | |
Error chi_cuadrado | ||||
10 | 0.897 | 0.896 | 0.842 | 0.843 |
25 | 0.907 | 0.907 | 0.898 | 0.895 |
100 | 0.904 | 0.921 | 0.901 | 0.917 |
250 | 0.901 | 0.902 | 0.901 | 0.901 |
500 | 0.899 | 0.892 | 0.897 | 0.892 |
1000 | 0.913 | 0.912 | 0.913 | 0.912 |
1500 | 0.916 | 0.896 | 0.916 | 0.895 |
2000 | 0.900 | 0.906 | 0.900 | 0.906 |
3000 | 0.898 | 0.896 | 0.898 | 0.896 |
Error exponencial | ||||
10 | 0.921 | 0.904 | 0.854 | 0.846 |
25 | 0.907 | 0.889 | 0.892 | 0.872 |
100 | 0.910 | 0.898 | 0.909 | 0.896 |
250 | 0.902 | 0.905 | 0.899 | 0.905 |
500 | 0.883 | 0.914 | 0.883 | 0.913 |
1000 | 0.919 | 0.907 | 0.919 | 0.907 |
1500 | 0.902 | 0.904 | 0.902 | 0.904 |
2000 | 0.891 | 0.910 | 0.890 | 0.910 |
3000 | 0.899 | 0.914 | 0.899 | 0.914 |
Error lognormal | ||||
10 | 0.892 | 0.899 | 0.831 | 0.832 |
25 | 0.913 | 0.898 | 0.894 | 0.881 |
100 | 0.897 | 0.918 | 0.894 | 0.917 |
250 | 0.916 | 0.897 | 0.916 | 0.894 |
500 | 0.914 | 0.916 | 0.913 | 0.914 |
1000 | 0.898 | 0.909 | 0.898 | 0.907 |
1500 | 0.913 | 0.891 | 0.913 | 0.891 |
2000 | 0.900 | 0.911 | 0.900 | 0.911 |
3000 | 0.886 | 0.913 | 0.886 | 0.913 |
Error t_student | ||||
10 | 0.915 | 0.900 | 0.853 | 0.829 |
25 | 0.907 | 0.892 | 0.888 | 0.885 |
100 | 0.914 | 0.905 | 0.908 | 0.905 |
250 | 0.902 | 0.913 | 0.902 | 0.912 |
500 | 0.911 | 0.908 | 0.911 | 0.908 |
1000 | 0.902 | 0.905 | 0.901 | 0.904 |
1500 | 0.895 | 0.901 | 0.895 | 0.901 |
2000 | 0.903 | 0.899 | 0.902 | 0.899 |
3000 | 0.892 | 0.886 | 0.892 | 0.886 |
Error uniforme | ||||
10 | 0.892 | 0.892 | 0.847 | 0.837 |
25 | 0.904 | 0.922 | 0.890 | 0.911 |
100 | 0.892 | 0.893 | 0.887 | 0.891 |
250 | 0.905 | 0.899 | 0.905 | 0.896 |
500 | 0.916 | 0.904 | 0.915 | 0.903 |
1000 | 0.900 | 0.887 | 0.900 | 0.887 |
1500 | 0.908 | 0.893 | 0.908 | 0.893 |
2000 | 0.887 | 0.902 | 0.887 | 0.902 |
3000 | 0.919 | 0.892 | 0.919 | 0.892 |
* En rojo los escenarios donde menos del 89% de los intervalos contuvo al parámetro. |
Para cada tipo de escenario, con las 1000 repeticiones, comparamos la distribución de \(\hat{ \beta }_{2}\) en cada escenario con la distribución normal:
datos %>%
select(n_obs, tipo_de_error, beta_2_est) %>%
mutate(
tipo_de_error = map_chr(tipo_de_error, ~glue("Error\n{.x}")),
n_obs_factor = map_chr(n_obs, ~ glue("n = {.x}"))
) %>%
ggplot() +
facet_grid(fct_reorder(n_obs_factor, n_obs) ~ tipo_de_error, scales = "free") +
aes(sample = beta_2_est) +
stat_qq(size = .5) +
stat_qq_line(color = "Red", alpha = .7) +
theme_minimal() +
theme(
axis.text = element_blank(),
axis.ticks = element_blank()
) +
labs(
title = "Distribución del β2 estimado comparada con una distribución normal",
subtitle = "1.000 observaciones en cada grupo",
x = "", y = ""
)
Respuesta: Las distribuciones de β2 estimado, especialmente cuando \(n\) no es demasiado chico, se parecen bastante a la normal.
Otra manera de visualizarlo:
library(bayestestR)
BETA_2_VERDADERO <- -3
beta_2_comparacion <-
datos %>%
select(n_obs, tipo_de_error, beta_2_est) %>%
group_by(n_obs, tipo_de_error) %>%
mutate(
desvio_beta_2 = sd(beta_2_est),
# beta_2_normal = rnorm(n(), mean = BETA_2_VERDADERO, sd = desvio_beta_2)
beta_2_normal = distribution_normal(n(), mean = BETA_2_VERDADERO, sd = desvio_beta_2)
) %>%
ungroup() %>%
select(-desvio_beta_2) %>%
gather("origen", "beta_value", -n_obs, -tipo_de_error) %>%
mutate(
escenario = map2_chr(n_obs, tipo_de_error, ~ glue("Error {.y}\nn = {.x}")),
escenario = fct_reorder(escenario, n_obs),
origen = ifelse(origen == "beta_2_est", "β2 estimado", "β2 de distribución normal")
)
beta_2_comparacion %>%
ggplot() +
facet_wrap(escenario ~ ., scales = "free", ncol = 5) +
aes(x = beta_value, color = origen, linetype = origen, size = origen) +
# geom_density() +
geom_line(stat = "density") +
geom_vline(xintercept = BETA_2_VERDADERO, size = .1) +
scale_color_manual(values = c("Tomato", "Black")) +
scale_size_manual(values = c(.8, .55)) +
scale_linetype_manual(values = c("solid", "solid")) +
theme_minimal() +
labs(
title = "Distribución del β2 estimado comparada con una distribución normal",
subtitle = "1.000 observaciones en cada grupo",
y = "", x = "", color = "", linetype = "", size = ""
) +
theme(
axis.text = element_blank(),
axis.ticks = element_blank(),
legend.position = "top"
)