Usamos os dados coletados por Andryw Marques para o mestrado dele. Cada medição reflete os hábitos de uma pessoa usando o last.fm durante um semestre. Usaremos apenas um atributo que crio abaixo: o número (médio) de artistas escutados por mês pela pessoa.
lastfm = read_csv(
here::here("data/experimento-lastfm.csv"),
col_types = cols(.default = col_double(),
user = col_character())
)
lastfm = select(lastfm, news, old, ecletic) %>% filter(!is.na(news + old)) %>% mutate(artists = (news + old)/6)
glimpse(lastfm)
## Rows: 11,988
## Columns: 4
## $ news <dbl> 38, 32, 24, 36, 28, 25, 30, 28, 14, 22, 27, 21, 23, 37, 25, 3…
## $ old <dbl> 116, 130, 10, 46, 128, 95, 228, 53, 46, 118, 90, 34, 100, 109…
## $ ecletic <dbl> 1159.4960, 3439.8373, 1611.4464, 697.0974, 1660.1563, 1907.90…
## $ artists <dbl> 25.666667, 27.000000, 5.666667, 13.666667, 26.000000, 20.0000…
lastfm %>% ggplot(aes(artists)) + geom_histogram(binwidth = 5, boundary = 0)
Imagine por agora que os dados que temos do Last.fm são completos. Que são a população inteira de usuários. Se o que nos interessa por exemplo é a média dessa população, ela é exatamente:
THETA = lastfm %>%
pull(artists) %>%
mean() # theta: média calculada com todos os dados
THETA
## [1] 22.14312
Como seria nossa visão dos dados se tivéssemos apenas uma amostra dos dados? sample_n(x, n) faz uma amostra aleatórioa de n elementos tirados do vetor x:
Se calcularmos a média do números de novos artistas escutados para três amostras de 100 elementos, teremos 3 resultados diferentes (3 \(\hat{\theta}\) diferentes):
lastfm %>% sample_n(size = 100) %>% pull(artists) %>% mean()
## [1] 21.065
lastfm %>% sample_n(100) %>% pull(artists) %>% mean()
## [1] 22.005
lastfm %>% sample_n(100) %>% pull(artists) %>% mean()
## [1] 21.91333
Se fizermos isso muitas vezes vemos como essa variação de \(\hat{\theta}\) acontece. A distribuição dos valores de uma estatística em diferentes amostras de uma população se chama distribuição amostral da estatística.
set.seed(1)
amostra_theta_c = function(df, n = 100) {
df %>%
sample_n(n) %>%
pull(artists) %>%
mean()
}
amostras = tibble(amostra = 1:1000) %>% # faremos 1000 vezes
mutate(theta_c = map_dbl(amostra, ~ amostra_theta_c(lastfm)))
amostras
amostras %>%
ggplot(aes(theta_c)) +
geom_histogram(binwidth = .5,
fill = "white",
colour = "darkgrey") +
geom_vline(xintercept = THETA, linetype = "dashed") +
labs(title = "Distribuição amostral")
Podemos ver também qual a distribuição do erro amostral:
amostras = amostras %>%
mutate(erro = theta_c - THETA)
amostras
amostras %>%
ggplot(aes(erro)) +
geom_histogram(binwidth = .5,
fill = "white",
colour = "darkblue") +
geom_vline(xintercept = 0, linetype = "dashed") +
labs(title = "Distribuição do erro amostral")
Veja que conhecendo a distribuição de \(\delta = \hat{\theta} - \theta\) nós conhecemos como as visões de \(\theta\) a partir de amostras variam. Com isso nós sabemos quão longe \(\hat{\theta}\) geralmente está de \(\theta\) e vice-versa.
Na prática: usando a distribuição nós conseguimos encontrar 2 valores de \(\delta = \hat{\theta} - \theta\) entre os quais os quais \(\hat{\theta} - \theta\) está 90% do tempo. Basta encontrar o 5 e o 95 percentis. Esses são os dois valores mais próximos que contém 90% das observações.
intervalo = amostras %>%
summarise(erro_i = quantile(erro, .05),
erro_s = quantile(erro, .95))
intervalo
Esse esse intervalo ao redor de \(\theta\) é o intervalo onde \(\hat{\theta}\) está 90% do tempo:
intervalo = intervalo %>%
mutate(valor_i = THETA + erro_i,
valor_s = THETA + erro_s)
intervalo
amostras %>%
mutate(no_intervalo = theta_c >= intervalo$valor_i &
theta_c <= intervalo$valor_s) %>%
summarise(cobertura = sum(no_intervalo) / n())
ggplot() +
geom_rect(
data = intervalo,
aes(xmin = valor_i, xmax = valor_s),
ymin = -Inf,
ymax = Inf,
fill = "coral",
alpha = .25
) +
geom_histogram(
data = amostras,
aes(x = theta_c),
binwidth = .5,
fill = "white",
colour = "darkgrey"
) +
geom_vline(xintercept = THETA, linetype = "dashed") +
labs(title = expression("Intervalo na distribuição amostral ao redor de" ~ theta))
O pulo do gato é entender que quando sabemos dizer se \(\theta\) está perto de \(\hat{\theta}\), sabemos também dizer o inverso. Então se há 90% dos \(\hat{\theta}\) que estão entre \([\theta + \sigma_{.05}; \theta + \sigma_{.95}]\), isso também significa que para 90% dos \(\hat{\theta}\), \(\theta\) está a \([\hat{\theta} + \sigma_{.05}; \hat{\theta} + \sigma_{.95}]\).
Partindo dos \(\hat{\theta}\) que calculamos no início de nosso exercício, podemos ver como esse método funciona:
ic_amostras = amostras %>%
mutate(intervalo_i = theta_c + intervalo$erro_i,
intervalo_s = theta_c + intervalo$erro_s) %>%
mutate(contem_theta = THETA >= intervalo_i & THETA <= intervalo_s)
ic_amostras %>%
sample_n(10)
ic_amostras %>%
summarise(cobertura = sum(contem_theta) / n())
ic_amostras %>%
sample_n(50) %>%
mutate(i = 1:n()) %>%
ggplot(aes(
x = i,
y = theta_c,
ymin = intervalo_i,
ymax = intervalo_s,
color = contem_theta
)) +
geom_pointrange(alpha = .8, size = .3) +
geom_hline(yintercept = THETA, color = "dark blue") +
labs(x = "amostra",
y = "média") +
scale_color_manual(values = c("red", "grey70"))
A mesma lógica vale para outras estatísticas além da média. O código abaixo analisa a distribuição dos valores observados em amostras a partir das quais calculamos a mediana. Altere o código para usar outra estatística: (dica: max e min não funcionam.)
funcao_theta = function(df) {
df %>%
pull(news) %>%
median()
}
THETA_OUTRO = funcao_theta(lastfm)
amostras = tibble(amostra = 1:1000) %>% # faremos 1000 vezes
mutate(theta_c = map_dbl(amostra, ~ lastfm %>%
sample_n(100) %>%
funcao_theta()))
amostras
amostras %>%
ggplot(aes(theta_c)) +
geom_histogram(binwidth = 1, fill = "white", colour = "darkgrey") +
geom_vline(xintercept = THETA_OUTRO)
amostras = amostras %>%
mutate(erro = theta_c - THETA_OUTRO)
amostras %>%
ggplot(aes(erro)) +
geom_histogram(binwidth = 1,
fill = "white",
colour = "darkblue") +
geom_vline(xintercept = 0, linetype = "dashed") +
labs(title = "Distribuição do erro amostral")
margem_erro = amostras %>%
summarise(baixo = quantile(erro, .05),
cima = quantile(erro, .95))
margem_erro
amostras %>%
sample_n(1) %>%
mutate(ic_inferior = theta_c + margem_erro$baixo,
ic_superior = theta_c + margem_erro$cima)
E se o tamanho da amostra (n) fosse muito menor?
amostras = data.frame(amostra = 1:1000) %>% # faremos 1000 vezes
mutate(media = map_dbl(amostra, ~ lastfm %>%
pull(artists) %>%
sample(100) %>%
mean()))
amostras %>% summary
## amostra media
## Min. : 1.0 Min. :18.72
## 1st Qu.: 250.8 1st Qu.:21.30
## Median : 500.5 Median :22.08
## Mean : 500.5 Mean :22.13
## 3rd Qu.: 750.2 3rd Qu.:22.91
## Max. :1000.0 Max. :26.29
amostras %>%
ggplot(aes(media)) +
geom_histogram(binwidth = 1, fill = "white", colour = "darkgrey") +
geom_vline(xintercept = THETA)
Espero que até agora tenha ficado claro que tendo a distribuição dos valores de uma estatística \(\hat{\theta}\) a partir de amostras (a distribuição amostral) e o valor de \(\hat{\theta}\), nós conseguimos estimar um intervalo com um método que acerta com uma cobertura conhecida. Conseguimos estimar uma margem de erro a partir de nosso \(\hat{\theta}\) com uma confiança conhecida.
Nós nunca temos várias amostras da população como nos exemplos até agora. Estamos aqui porque nós não temos a população, e todo o dado que temos forma uma amostra. Caso não tenha ficado claro: o exercício até agora foi simular o que aconteceria se tivéssemos várias amostras para fins pedagógicos.
Não temos a população para estimar a distribuição amostral e a distribuição do erro amostral. Mas temos algo que veio dessa população, que é a amostra. Em várias situações como essa, a Estatística contorna a falta da informação ideal usando a que temos e um método que funcione bem assim.
A ideia principal que usaremos é uma técnica chamada boostrapping que funciona porque usar a amostra como substituto da população e simular a amostragem através de reamostragem com reposição fornece uma estimativa precisa da variação na distribuição amostral.
Pegando por partes:
Como resultado, teremos uma distribuição de como \(\hat{\theta}*\) varia.
O princípio do bootstrap diz que a variação de \(\hat{\theta}*\) nos bootstraps aproxima a variação de \(\hat{\theta}\).
Com isso, podemos usar a mesma lógica que usamos acima e construir um intervalo ao retor de \(\hat{\theta}\) que contém \(\theta\) com uma certa confiança.
\(P(\delta_{0.05} \le e_a - e_p \le \delta_{0.95} | e_p) = .9\). Manipulando, temos que \(P(e_a - \delta_{0.05} \ge e_p \ge e_a - \delta_{0.95} | e_p) = .9\). Ou seja, o IC é \([e_a - \delta_{0.05}; e_a - \delta_{0.95}]\).
glimpse(lastfm)
## Rows: 11,988
## Columns: 4
## $ news <dbl> 38, 32, 24, 36, 28, 25, 30, 28, 14, 22, 27, 21, 23, 37, 25, 3…
## $ old <dbl> 116, 130, 10, 46, 128, 95, 228, 53, 46, 118, 90, 34, 100, 109…
## $ ecletic <dbl> 1159.4960, 3439.8373, 1611.4464, 697.0974, 1660.1563, 1907.90…
## $ artists <dbl> 25.666667, 27.000000, 5.666667, 13.666667, 26.000000, 20.0000…
funcao_theta = function(df) {
df %>%
pull(artists) %>%
mean()
}
THETA = funcao_theta(lastfm)
set.seed(1212)
amostra = lastfm %>%
sample_n(50)
theta_c = funcao_theta(amostra)
theta_c
## [1] 24.08667
repeticoes = 4000 # pelo menos 2000, mas mais não faz mal.
um_bootstrap <- function(x){
dados = x %>% pull(artists)
boot_x <- sample(dados, # amostre dos dados
size = NROW(dados), # tamanho igual ao recebido
replace = TRUE) # aqui é o bootstrap
return(mean(boot_x))
}
set.seed(1212)
# A REAMOSTRAGEM
reamostragens = tibble(i = 1:repeticoes) %>%
mutate(theta_c_s = map_dbl(i, ~ um_bootstrap(amostra)))
reamostragens
reamostragens %>%
ggplot(aes(x = theta_c_s)) +
geom_histogram(binwidth = .5,
colour = "darkorange",
fill = "white") +
labs(title = "Distribuição nos bootstraps",
subtitle = "É nossa estimativa da distribuição amostral")
reamostragens %>%
ggplot(aes(x = theta_c_s - theta_c)) +
geom_histogram(binwidth = 1,
colour = "darkblue",
fill = "white") +
labs(title = "Distribuição do erro amostral estimada via bootstraps")
Agora usamos a distribuição de \(\delta* = \hat{\theta}* - \hat{\theta}\) no lugar da de \(\delta\).
intervalo = reamostragens %>%
mutate(erro = theta_c_s - theta_c) %>%
summarise(erro_i = quantile(erro, .05),
erro_s = quantile(erro, .95))
intervalo
Agora fazemos o mesmo que antes para estimar onde \(\theta\) está usando \(\hat{\theta}\).
intervalo = intervalo %>%
mutate(valor_i = theta_c + erro_i,
valor_s = theta_c + erro_s)
intervalo
ggplot() +
geom_rect(
data = intervalo,
aes(xmin = valor_i, xmax = valor_s),
ymin = -Inf,
ymax = Inf,
fill = "gold",
alpha = .25
) +
annotate(
geom = "text",
x = intervalo$valor_i,
y = -10,
label = paste("hat(theta) + MoE_i"),
parse = T,
color = "brown",
size = 3
) +
annotate(
geom = "text",
x = intervalo$valor_s,
y = -10,
label = paste("hat(theta) + MoE_s"),
parse = T,
color = "brown",
size = 3
) +
geom_histogram(
data = reamostragens,
aes(theta_c_s),
binwidth = .5,
fill = "white",
colour = "darkgrey"
) +
geom_vline(xintercept = THETA,
color = "dodgerblue",
size = 1.2) +
annotate(
geom = "text",
x = THETA - .3,
y = -10,
label = "theta",
parse = T,
color = "dodgerblue",
size = 5
) +
geom_vline(xintercept = theta_c, color = "brown") +
annotate(
geom = "text",
x = theta_c - .3,
y = -10,
label = "hat(theta)",
parse = T,
color = "brown",
size = 5
) +
labs(title = expression("Intervalo estimado via bootstrap"))
Com outro nível de confiança:
confianca = .99
alpha = 1 - confianca
intervalo2 = reamostragens %>%
mutate(erro = theta_c_s - theta_c) %>%
summarise(erro_i = quantile(erro, alpha / 2),
erro_s = quantile(erro, 1 - alpha /2)) %>%
mutate(valor_i = theta_c + erro_i,
valor_s = theta_c + erro_s)
intervalo2
ggplot() +
geom_rect(
data = intervalo2,
aes(xmin = valor_i, xmax = valor_s),
ymin = -Inf,
ymax = Inf,
fill = "coral",
alpha = .25
) +
geom_rect(
data = intervalo,
aes(xmin = valor_i, xmax = valor_s),
ymin = -Inf,
ymax = Inf,
fill = "coral2",
alpha = .5
) +
geom_histogram(
data = reamostragens,
aes(theta_c_s),
binwidth = .5,
fill = "white",
colour = "darkgrey"
) +
geom_vline(xintercept = THETA,
color = "dodgerblue",
size = 1.5) +
geom_vline(xintercept = theta_c, color = "brown") +
labs(title = expression("Intervalo estimado via bootstrap"),
subtitle = "Escuro: 99%, claro: 90% confiança",
x = expression(hat(theta) ~ "*"))
intervalo = intervalo %>%
mutate(theta_c = theta_c)
intervalo %>%
ggplot(aes(x = "", y = theta_c, ymin = valor_i, ymax = valor_s)) +
geom_linerange() +
geom_point(size = 2) +
coord_flip() +
scale_y_continuous(limits = c(15, 30)) +
labs(
title = "IC da média de artistas escutados",
y = "Média de artistas escutados por mês",
x = "")