28 Inferenza sul modello lineare
Un modo per rappresentare l’incertezza dell’inferenza in un ottica bayesiana è quella di presentare graficamente la retta specificata dal modello di regressione lineare. Continuerò qui la discussione dell’esempio descritto nel Capitolo precedente, ovvero, userò i dati kid_score
e i valori mom_iq
centrati.
28.1 Rappresentazione grafica dell’incertezza della stima
Supponiamo (come indicato nel Capitolo precedente) di avere eseguito il campionamento MCMC mediante la seguente istruzione.
Codice
fit2 <- mod$sample(
data = data2_list,
iter_sampling = 4000L,
iter_warmup = 2000L,
seed = SEED,
chains = 4L,
refresh = 0
)
Per creare una rappresentazione grafica della retta di regressione stimata dal modello bayesiano, insieme all’incertezza della stima, è necessario manipolare i dati contenuti nell’oggetto creato da mod$sample()
che contiene i campioni a posteriori dei parametri del modello di regressione lineare, ovvero fit2
.
Usando la funzione rstan::read_stan_csv()
trasformo fit2
in un oggetto di formato stanfit
.
Codice
output_stanfit <- rstan::read_stan_csv(fit2$output_files())
Dall’oggetto output_stanfit
estraggo i campioni a posteriori dei parametri extract()
.
Codice
post <- rstan::extract(output_stanfit)
L’oggetto post
così creato è una lista.
Codice
class(post)
#> [1] "list"
Esaminiamo il contenuto di post
.
Codice
glimpse(post)
#> List of 7
#> $ alpha_std: num [1:16000(1d)] 0.0582 0.0407 -0.0828 0.0997 0.0886 ...
#> ..- attr(*, "dimnames")=List of 1
#> .. ..$ iterations: NULL
#> $ beta_std : num [1:16000(1d)] 0.421 0.494 0.457 0.496 0.491 ...
#> ..- attr(*, "dimnames")=List of 1
#> .. ..$ iterations: NULL
#> $ sigma_std: num [1:16000(1d)] 0.907 0.899 0.878 0.932 0.972 ...
#> ..- attr(*, "dimnames")=List of 1
#> .. ..$ iterations: NULL
#> $ alpha : num [1:16000(1d)] 88 87.6 85.1 88.8 88.6 ...
#> ..- attr(*, "dimnames")=List of 1
#> .. ..$ iterations: NULL
#> $ beta : num [1:16000(1d)] 0.573 0.672 0.622 0.675 0.668 ...
#> ..- attr(*, "dimnames")=List of 1
#> .. ..$ iterations: NULL
#> $ sigma : num [1:16000(1d)] 18.5 18.3 17.9 19 19.8 ...
#> ..- attr(*, "dimnames")=List of 1
#> .. ..$ iterations: NULL
#> $ lp__ : num [1:16000(1d)] -170 -169 -171 -172 -174 ...
#> ..- attr(*, "dimnames")=List of 1
#> .. ..$ iterations: NULL
L’output di glimpse()
ci dice che alpha
è un vettore di 16,000 elementi. Ciascuno di questi elementi è un valore estratto a caso dalla distribuzione a posteriori del parametro
Codice
mean(post$alpha)
#> [1] 86.7936
Lo stesso si può dire di beta
.
Codice
mean(post$beta)
#> [1] 0.6082648
Per creare un diagramma a dispersione dei dati con sovrapposto il valore atteso della
Codice
Si noti l’uso della funzione geom_abline()
che prende come argomenti l’intercetta e la pendenza di una retta. Nel caso presente, tali argomenti corrispondono a mean(post$alpha)
e mean(post$beta)
, ovvero, specificano i valori a posteriori più plausibili dei parametri
Con le istruzioni precedenti abbiamo disegnato una singola retta. Ma una singola retta non ci fa capire qual è l’incertezza associata alle stime dei parametri
Per fare ciò dobbiamo estrarre le informazioni richieste dall’oggetto output_stanfit
che è stato creato. A tal fine possiamo usare, ad esempio, le funzioni del pacchetto tidybayes
. Iniziamo a elencare i nomi degli oggetti contenuti in output_stanfit
.
Codice
tidybayes::get_variables(output_stanfit)
#> [1] "alpha_std" "beta_std" "sigma_std" "alpha"
#> [5] "beta" "sigma" "lp__" "accept_stat__"
#> [9] "treedepth__" "stepsize__" "divergent__" "n_leapfrog__"
#> [13] "energy__"
Vogliamo creare un DataFrame in formato tidy, cioè, tale per cui le osservazioni stanno sulle righe e le variabili stanno sulle colonne; una colonna per le stime a posteriori di spread_draws()
.
Codice
draws <- output_stanfit %>%
spread_draws(beta, alpha)
Esaminiamo l’oggetto draws
.
Codice
draws %>%
head(10)
#> # A tibble: 10 × 5
#> .chain .iteration .draw beta alpha
#> <int> <int> <int> <dbl> <dbl>
#> 1 1 1 1 0.632 88.4
#> 2 1 2 2 0.491 87.5
#> 3 1 3 3 0.717 85.9
#> 4 1 4 4 0.478 87.5
#> 5 1 5 5 0.610 86.4
#> 6 1 6 6 0.570 86.7
#> 7 1 7 7 0.623 87.0
#> 8 1 8 8 0.616 87.2
#> # … with 2 more rows
L’oggetto draws
contiene le stime a posteriori dei parametri ggplot()
a cui vengono aggiunte tutte le 16,000 rette di regressione definite da ciascuna coppia di valori draws
.
Codice
tibble(
kid_score = df$kid_score,
mom_iq = df$mom_iq - mean(df$mom_iq)
) %>%
ggplot(aes(mom_iq, kid_score)) +
geom_point() +
geom_abline(
data = draws,
aes(intercept = alpha, slope = beta),
size = 0.2, alpha = 0.01, color = "darkgray"
) +
geom_abline(
intercept = mean(post$alpha),
slope = mean(post$beta)
) +
labs(
x = "Quoziente di intelligenza della madre",
y = "Quoziente di intelligenza del bambino"
)
Il risultato cercato si ottiene (disegnare molteplici rette ciascuna definita da un valore casuale dalla distribuzione a posteriori dei parametri
Codice
geom_abline(
data = draws,
aes(intercept = alpha, slope = beta),
size = 0.2, alpha = 0.01, color = "darkgray"
)
L’argomento grafico alpha = 0.01
passato a geom_abline()
specifica la trasparenza del segmento che rappresenta ciascuna retta. Ho usato un valore molto basso per questo argomento per fare in modo che, anche sovrapponendo 16,000 rette, si produca comunque ancora un certo grado di trasparenza.
Il grafico mostra che le rette di regressione costruite estraendo a caso valori dalla distribuzione a posteriori dei parametri
Si presti attenzione al fatto che il modello statistico ci conduce a tale conclusione: siamo sicuri dell’esistenza di un’associazione positiva tra il QI dei figli e il QI della madre. Ma il modello statistico non ci dice nulla sulle cause di questa associazione: ci dice soltanto che le due variabili tendono a covariare. Non ci dice che il QI della madre è la “causa” del QI del figlio. Questo è un argomento su cui è stata fatta molta ricerca (e di ciò qui non diremo nulla). Ma, al di là dei risultati di tali ricerche, se consideriamo solo il risultato del modello statistico qui esaminato, nulla si può concludere sui rapporti di causa/effetto tra QI della madre e QI del figlio. La presenza di un’associazione statistica, infatti, è condizione necessaria ma non sufficiente per potere affermare l’esistenza di un nesso causale.
28.2 Intervalli di credibilità
Abbiamo visto come l’incertezza sulla stima dei parametri possa essere espressa graficamente. In alternativa, l’incertezza inferenziale sui parametri può essere descritta mediante gli intervalli di credibilità, ovvero gli intervalli che contengono la quota desiderata (es., il 95%) della distribuzione a posteriori.
Per l’esempio che stiamo discutendo, gli intervalli di credibilità (a code uguali) al 95% si ottengono nel modo seguente:
Codice
rstantools::posterior_interval(
as.matrix(output_stanfit),
prob = 0.95
)
#> 2.5% 97.5%
#> alpha_std -0.08427372 0.08441589
#> beta_std 0.36136782 0.53165187
#> sigma_std 0.83902970 0.96033440
#> alpha 85.07713000 88.52020750
#> beta 0.49171840 0.72342520
#> sigma 17.12519250 19.60110750
#> lp__ -173.15907500 -168.54400000
Un grafico che, nel caso dei dati standardizzati, riporta l’intervallo di credibilità al livello di probabilità desiderato per i parametri
Codice
output_stanfit %>%
mcmc_intervals(
pars = c("alpha_std", "beta_std", "sigma_std"),
prob = 0.8,
prob_outer = 0.95
)
Gli intervalli di massima densità si trovano nel modo seguente.
Codice
bayestestR::hdi(output_stanfit, ci = 0.95)
#> Highest Density Interval
#>
#> Parameter | 95% HDI
#> --------------------------
#> alpha_std | [-0.08, 0.08]
#> beta_std | [ 0.36, 0.53]
#> alpha | [85.08, 88.52]
#> beta | [ 0.49, 0.72]
Quando la distribuzione a posteriori dei parametri è simmetrica, i due tipi di intervalli producono, all’atto pratico, risultati equivalenti.
28.2.1 Quale soglia usare?
Ripeto c’è niente di “magico” o necessario relativamente al livello di 0.95: il valore 0.95 è arbitrario. È quello utilizzato nelle pubblicazioni scientifiche, di consuetudine. Almeno in psicologia. In fisica, ad esempio, si usa un intervallo molto più grande.
Kennedy-Shaffer (2019) descrivono l’origine storica di questa scelta. Nel 1925 Ronal Fisher pubblicò la prima edizione della sua influente opera Statistical Methods for Research Workers. In tale testo troviamo il seguente passaggio:
The value for which P=.05, or 1 in 20, is 1.96 or nearly 2; it is convenient to take this point as a limit in judging whether a deviation is to be considered significant or not. Deviations exceeding twice the standard deviation are thus formally regarded as significant. Using this criterion we should be led to follow up a negative result only once in 22 trials, even if the statistics are the only guide available. Small effects would still escape notice if the data were insufficiently numerous to bring them out, but no lowering of the standard of significance would meet this difficulty (Fisher, 1925, p. 47)
Questo paragrafo rende immediatamente evidente il motivo per cui Fisher afferma che il valore 0.05 è conveniente: è più o meno equivalente alla probabilità di trovarsi a più di due deviazioni standard dalla media di una variabile casuale normalmente distribuita. In questo modo, 0.05 può essere visto non come un numero dotato in un qualche significato importante, ma solo come un valore che risultava dalla necessità di facilità di calcolo, prima che i computer rendessero obsolete le tabelle e le approssimazioni. In seguito, nel discutere le applicazioni statistiche della distribuzione
[w]e shall not often be astray if we draw a conventional line at .05, and consider that higher values of
indicate a real discrepancy (Fisher, 1925, p. 79).
Sulla base di queste affermazioni di Fisher, la soglia del 0.95 è diventata la consuetudine nella comunità scientifica – o almeno, in parte di essa.
Ma sono ovviamente possibili tantissime altre soglie per quantificare la nostra incertezza: alcuni ricercatori usano il livello di 0.89, altri quello di 0.5. Se l’obiettivo è quello di descrivere il livello della nostra incertezza relativamente alla stima del parametro, allora dobbiamo riconoscere che la nostra incertezza è descritta dall’intera distribuzione a posteriori. Per cui il metodo più semplice, più diretto e più completo per descrivere la nostra incertezza rispetto alla stima dei parametri è quello di riportare graficamente tutta la distribuzione a posteriori. Per l’esempio presente, una rappresentazione della distribuzione a posteriori dei parametri del modello si ottiene, ad esempio, con la seguente istruzione.
In alternativa possiamo usare la seguente istruzione.
Codice
mcmc_areas(
fit2$draws(c("alpha_std", "beta_std", "sigma_std")),
prob = 0.8,
prob_outer = 0.95
)
28.3 Test di ipotesi
È facile valutare ipotesi direzionali usando Stan. Per esempio, chiediamoci quale sia la probabilità
Per trovare la probabilità richiesta possiamo usare il vettore post$beta
il quale contiene 16,000 valori presi a caso dalla distribuzione a posteriori post$beta > 0
valuta se ciascun elemento di post$beta
soddisfi la condizione logica specificata, ritornando TRUE
(codificato con 1) o FALSE
(codificato con 0) a seconda che la condizione logica sia vera o falsa. L’istruzione sum(post$beta > 0)
conta dunque il numero di volte in cui la condizione è soddisfatta, mentre length(post$beta)
è uguale a 16,000. La proporzione così determinata è una stima empirica della probabilità cercata.
L’evento complementare, ovvero, la probabilità
Ciò significa che, relativamente alla presenza di un’associazione lineare positiva tra QI della madre e QI del figlio, la forza dell’evidenza è enorme.
28.4 Modello lineare robusto
Spesso i ricercatori devono affrontare il problema degli outlier (osservazioni anomale): in presenza di outlier, un modello statistico basato sulla distribuzione gaussiana produce delle stime distorte dei parametri (ovvero stime che non si generalizzano ad altri campioni di dati). Il metodo tradizionale per affrontare questo problema è quello di eliminare gli outlier prima di eseguire l’analisi statistica. Il problema di questo approccio, però, è che il criterio utilizzato per eliminare gli outlier, quale esso sia, è arbitrario; dunque, usando criteri diversi per la rimozione di outlier, i ricercatori finiscono per trovare risultati diversi.
Questo problema trova una semplice soluzione nell’approccio bayesiano. Il modello lineare che abbiamo dicusso finora ipotizza una specifica distribuzione degli errori, ovvero
Per fare un esempio, introduciamo un singolo valore anomalo e influente nel set dei dati dell’esempio che stiamo discutendo:
Codice
df2 <- df
df2$kid_score[434] <- -500
df2$mom_iq[434] <- 140
Per comodità, calcoliamo le stime di
In generale, però, non è necessario assumere
Per verificare questa affermazione, modifichiamo il codice Stan usato in precedenza in modo tale da ipotizzare che student_t(nu, mu, sigma)
.1
Codice
modelString <- "
data {
int<lower=0> N;
vector[N] y;
vector[N] x;
}
transformed data {
vector[N] x_std;
vector[N] y_std;
x_std = (x - mean(x)) / sd(x);
y_std = (y - mean(y)) / sd(y);
}
parameters {
real alpha_std;
real beta_std;
real<lower=0> sigma_std;
real<lower=1> nu; // degrees of freedom is constrained >1
}
model {
alpha_std ~ normal(0, 1);
beta_std ~ normal(0, 1);
sigma_std ~ normal(0, 1);
nu ~ gamma(2, 0.1); // Juárez and Steel(2010)
y_std ~ student_t(nu, alpha_std + beta_std * x_std, sigma_std);
}
generated quantities {
real alpha;
real beta;
real<lower=0> sigma;
alpha = sd(y) * (alpha_std - beta_std * mean(x) / sd(x)) + mean(y);
beta = beta_std * sd(y) / sd(x);
sigma = sd(y) * sigma_std;
}
"
writeLines(modelString, con = "code/simpleregstdrobust.stan")
Costruiamo la lista dei dati usando il data.frame df2
che include l’outlier:
Adattiamo il modello lineare robusto ai dati:
Codice
file <- file.path("code", "simpleregstdrobust.stan")
mod <- cmdstan_model(file)
fit4 <- mod$sample(
data = data3_list,
iter_sampling = 4000L,
iter_warmup = 2000L,
seed = SEED,
chains = 4L,
refresh = 0
)
Se esaminiamo le stime dei parametri notiamo che la stima di
Codice
fit4$summary(c("alpha", "beta", "sigma", "nu"))
#> # A tibble: 4 × 10
#> variable mean median sd mad q5 q95 rhat ess_bulk ess_tail
#> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 alpha 87.8 87.8 0.901 0.898 86.3 89.3 1.00 14740. 12422.
#> 2 beta 0.602 0.602 0.0589 0.0587 0.505 0.699 1.00 14903. 11582.
#> 3 sigma 15.9 15.9 0.800 0.803 14.6 17.2 1.00 12993. 11619.
#> 4 nu 5.58 5.46 1.15 1.09 3.93 7.64 1.00 12998. 11288.
I risultati mostrano come il modello lineare robusto non risente della presenza di outlier (almeno nel caso presente).
Commenti e considerazioni finali
Nell’approccio bayesiano possiamo rappresentare l’incertezza delle nostre credenze a posteriori in due modi: mediante la rappresentazione grafica dell’intera distribuzione a posteriori dei parametri o mediante l’uso degli intervalli di credibilità. Un bonus della discussione del presente Capitolo è quello di mostrare come il modello lineare tradizionale (che assume
È equivalente scrivere
, dove oppure ↩︎