57  Modello di Poisson (1)

“Statistical analysis is a dialogue between models and data: we propose a model, confront it with data, and revise our understanding.”

Andrew Gelman

Introduzione

In questo capitolo affrontiamo un primo esempio di modellazione con Stan, il linguaggio che useremo per costruire modelli statistici e fare inferenza bayesiana. Riprendiamo un caso già risolto con metodi analitici e con il metodo su griglia — il modello Gamma–Poisson — e lo affrontiamo ora con il campionamento MCMC. In questo modo possiamo confrontare i risultati e, soprattutto, imparare un workflow che useremo anche quando non esistono soluzioni chiuse.

Panoramica del capitolo

  • Introdurre il modello di Poisson per dati di conteggio.
  • Specificare una distribuzione a priori Gamma coniugata.
  • Implementare il modello in Stan e stimare i parametri con MCMC.
  • Confrontare i risultati con la soluzione analitica Gamma–Poisson.
  • Applicare il modello a un dataset reale sulle sparatorie mortali negli Stati Uniti.

In questo capitolo non vogliamo solo calcolare una media dal campione, ma imparare a:

  • stimare il tasso medio di occorrenza nella popolazione a partire dai dati osservati;
  • esprimere anche la nostra incertezza su quel tasso, non solo un singolo numero;
  • tradurre un modello teorico (Poisson con prior Gamma) in un modello computazionale in Stan;
  • verificare che Stan fornisce gli stessi risultati della soluzione analitica, così da prendere confidenza con il workflow MCMC.

Questo esempio funziona come una “palestra”: semplice, ma ci prepara ad affrontare modelli più complessi in cui non avremo formule chiuse e l’uso di Stan diventerà indispensabile.

  • Molti dati psicologici si presentano come conteggi (episodi comportamentali, risposte corrette, eventi clinici).
  • Il modello di Poisson è lo strumento naturale per stimare il tasso medio di occorrenza di questi eventi.
  • L’approccio bayesiano ci permette non solo di stimare questo tasso, ma anche di esprimere in modo chiaro la nostra incertezza.
  • In questo capitolo usiamo il Poisson come primo esempio pratico per imparare a scrivere e stimare un modello in Stan.
here::here("code", "_common.R") |> 
  source()

if (!requireNamespace("pacman")) install.packages("pacman")
pacman::p_load(cmdstanr, posterior, bayesplot, ggplot2, tidyverse, tibble)
conflicts_prefer(posterior::ess_bulk)
conflicts_prefer(posterior::ess_tail)
conflicts_prefer(dplyr::count)

57.1 Dal modello teorico al modello computazionale

Quando raccogliamo dati in psicologia, non ci interessa solo descrivere quello che è accaduto nel nostro campione, ma soprattutto stimare il processo che genera i dati nella popolazione.

Immaginiamo di osservare, in otto finestre temporali di pari durata, quante volte si verifica un certo evento psicologico o comportamentale. Per esempio, il numero di compulsioni in otto momenti della giornata, oppure il numero di telefonate ricevute in otto turni orari.

I dati raccolti sono:

y <- c(2, 1, 3, 2, 2, 1, 1, 1)

Abbiamo quindi \(N = 8\) osservazioni. Dal campione potremmo calcolare una semplice media (\(\bar y = 1.625\)), ma questo ci dice solo quanto spesso l’evento è avvenuto nei nostri dati. La domanda più interessante è:

qual è il tasso medio di occorrenza nella popolazione?

Chiamiamo questo tasso \(\lambda\): il numero medio di eventi attesi in una finestra temporale.

57.1.1 Perché serve un modello probabilistico?

I dati osservati sono solo un piccolo campione e possono variare da una raccolta all’altra. Un modello probabilistico ci serve per due motivi principali:

  1. Separare il segnale dal rumore: capire quanto dell’andamento osservato è dovuto al caso e quanto riflette una regolarità della popolazione.
  2. Quantificare l’incertezza: non ci basta stimare \(\lambda\), vogliamo anche dire quanto siamo sicuri (o incerti) di quella stima.

57.1.2 Il modello di Poisson

Per i fenomeni di conteggio (quante volte un evento si verifica in un intervallo), il modello naturale è la distribuzione di Poisson:

\[ y_i \sim \text{Poisson}(\mu_i), \qquad \mu_i = \lambda \cdot t_i \]

dove:

  • \(y_i\) è il numero osservato di eventi nella finestra \(i\),
  • \(t_i\) è la durata della finestra,
  • \(\lambda\) è il tasso medio.

Nel nostro caso tutte le finestre hanno la stessa durata (\(t_i = 1\)), e quindi:

\[ P(y_i \mid \lambda) = \frac{\lambda^{y_i} e^{-\lambda}}{y_i!}, \qquad \mathbb{E}[y_i]=\lambda, \quad \mathrm{Var}(y_i)=\lambda. \]

In parole semplici: \(\lambda\) rappresenta quanti eventi in media ci aspettiamo per finestra.

Modelli di questo tipo compaiono spesso anche nella ricerca psicologica.
Alcuni esempi:

  • Clinica: numero di episodi compulsivi o attacchi di panico osservati in un certo periodo.
  • Psicologia dello sviluppo: numero di parole nuove prodotte da un bambino in un giorno.
  • Psicologia cognitiva: numero di risposte corrette in una serie di prove a tempo.
  • Psicologia sociale: numero di interazioni tra membri di un gruppo osservate in una sessione.

In tutti questi casi, il modello di Poisson ci aiuta a stimare il tasso medio di occorrenza dell’evento di interesse e l’incertezza che accompagna tale stima.

57.1.3 La distribuzione a priori

Nell’approccio bayesiano dobbiamo dichiarare che cosa riteniamo plausibile per \(\lambda\) prima di osservare i dati. Per i modelli di Poisson la scelta naturale è la distribuzione Gamma. Ad esempio, una prior Gamma(9, 2) esprime l’idea che, prima di osservare i dati, ci aspettiamo circa 4–5 eventi per finestra, ma lasciamo spazio a una certa variabilità.

57.1.4 Perché usiamo Stan?

Potremmo calcolare la distribuzione a posteriori anche con formule chiuse (qui la posterior è ancora una Gamma). Ma usiamo Stan per due motivi didattici fondamentali:

  1. Generalizzare: nella pratica incontreremo modelli per cui non esiste una soluzione analitica semplice. Con Stan impariamo un workflow valido in tutti i casi.
  2. Quantificare l’incertezza: Stan ci fornisce direttamente campioni dalla distribuzione a posteriori, che possiamo usare per costruire intervalli credibili, fare previsioni, confrontare modelli, ecc.

In questo esempio l’uso di Stan può sembrare eccessivo, perché sappiamo già che la posterior è una Gamma e potremmo calcolarla con una formula.
Il punto, però, è proprio esercitarsi: usiamo un caso semplice come “palestra” per imparare un workflow (specificare un modello, campionare con MCMC, analizzare la posterior) che ci sarà indispensabile quando i modelli diventeranno più realistici e complessi, e non avremo più scorciatoie analitiche.

57.1.5 Obiettivi del modello in Stan

Con questo primo esempio vogliamo:

  • stimare \(\lambda\), il tasso medio di occorrenza nella popolazione;
  • ottenere una misura della nostra incertezza su \(\lambda\);
  • verificare che i campioni generati da Stan coincidano con i risultati della formula analitica (dove è disponibile).

In questo modo ci abituiamo a un flusso di lavoro che useremo anche per modelli molto più complessi, in cui solo un approccio computazionale ci permette di fare inferenza bayesiana.

57.2 Scrivere il modello in Stan

Un modello Stan è diviso in blocchi: data (dati), parameters (incognite), model (priori + verosimiglianza). Aggiungiamo anche generated quantities per quantità derivate utili al confronto con la soluzione analitica (e, volendo, per LOO).

Per evitare dipendenze da percorsi locali, creiamo e compiliamo il modello direttamente dalla stringa:

stan_code <- "
data {
  int<lower=0> N;
  array[N] int<lower=0> y;
  real<lower=0> alpha_prior;
  real<lower=0> beta_prior;
}
parameters {
  real<lower=0> lambda;
}
model {
  lambda ~ gamma(alpha_prior, beta_prior);
  y ~ poisson(lambda);
}
generated quantities {
  real alpha_post = alpha_prior + sum(y);
  real beta_post  = beta_prior + N;
  array[N] real log_lik;
  for (i in 1:N) log_lik[i] = poisson_lpmf(y[i] | lambda);
}
"

Compiliamo:

mod <- cmdstan_model(write_stan_file(stan_code))

Prepariamo i dati:

N <- length(y)
alpha_prior <- 9
beta_prior  <- 2

stan_data <- list(N = N, y = y, alpha_prior = alpha_prior, beta_prior = beta_prior)

Una volta preparati i dati, lanciamo il campionamento:

fit <- mod$sample(
  data = stan_data,
  seed = 123,
  chains = 4,
  parallel_chains = 4,
  iter_sampling = 3000,
  iter_warmup = 2000,
  refresh = 0
)

57.2.1 Analizzare i risultati

Una volta che Stan ha terminato il campionamento, otteniamo migliaia di valori possibili di \(\lambda\), campionati dalla distribuzione a posteriori. Questi valori ci permettono di “vedere” la distribuzione a posteriori invece di calcolarla solo con una formula.

1. Estraiamo i campioni di \(\lambda\)

Estraiamo i valori campionati di λ dalla posterior. L’oggetto draws restituisce tutti i campioni MCMC, noi qui selezioniamo solo il parametro lambda:

posterior_draws <- fit$draws("lambda", format = "df")

Creiamo un vettore con solo i valori di lambda:

lambda_samples <- posterior_draws$lambda

Ora lambda_samples contiene migliaia di valori di \(\lambda\): possiamo usarli per calcolare media, intervalli credibili e per costruire grafici.

length(lambda_samples)
#> [1] 12000
head(lambda_samples)
#> [1] 1.679 1.519 1.477 2.829 2.840 3.013

2. Calcoliamo i parametri della distribuzione posteriore teorica

Dato che in questo caso abbiamo una posterior coniugata, conosciamo la formula esatta della distribuzione a posteriori (Gamma). Ci serve per confrontarla con i campioni generati da Stan:

alpha_post <- alpha_prior + sum(y)   # nuovo parametro shape
beta_post  <- beta_prior + N         # nuovo parametro rate

3. Confrontiamo i due risultati (MCMC vs formula)

Creiamo un grafico che mostri l’istogramma dei campioni ottenuti con Stan (in azzurro) e la curva della distribuzione Gamma calcolata analiticamente (in rosso).

ggplot(data.frame(lambda = lambda_samples), aes(x = lambda)) +
  # Istogramma dei campioni posteriori
  geom_histogram(
    aes(y = after_stat(density)),
    bins = 50,
    fill = okabe_ito["sky"],
    alpha = 0.7
  ) +
  # Curva teorica Gamma con parametri aggiornati
  stat_function(
    fun = function(x) dgamma(x, shape = alpha_post, rate = beta_post),
    color = okabe_ito["vermillion"],
    linewidth = 1.2
  ) +
  labs(
    title = "Distribuzione a posteriori di λ",
    x = "λ (tasso medio di occorrenza)",
    y = "Densità"
  )

Il confronto visivo è molto utile:

  • l’istogramma mostra la distribuzione stimata con Stan tramite campionamento MCMC,
  • la curva rossa rappresenta la distribuzione teorica Gamma–Poisson che conosciamo già.

Se le due coincidono (entro piccole fluttuazioni casuali), significa che Stan ha fatto un buon lavoro: il nostro workflow MCMC funziona!

57.2.2 Intervallo di credibilità

Un grande vantaggio dell’approccio bayesiano è che non otteniamo solo una stima puntuale di \(\lambda\), ma una distribuzione completa dei valori plausibili. Da questa distribuzione possiamo ricavare un intervallo di credibilità: un intervallo che contiene, ad esempio, il 94% o il 95% della massa a posteriori.

1. Calcoliamo i quantili della distribuzione

Calcoliamo i quantili al 3%, 50% (mediana) e 97%:

cred_interval <- quantile(lambda_samples, probs = c(0.03, 0.5, 0.97))
cred_interval
#>    3%   50%   97% 
#> 1.422 2.168 3.177
  • il valore al 50% è la mediana della distribuzione a posteriori,
  • i valori al 3% e 97% delimitano un intervallo centrale che contiene il 94% della distribuzione.

2. Interpretazione dei risultati

I risultati ottenuti indicano che:

  • il valore “tipico” di \(\lambda\) (mediana) è circa 2.2 eventi per finestra,
  • c’è un 94% di probabilità che \(\lambda\) si trovi tra 1.42 e 3.20.

L’informazione più importante non è tanto la stima puntuale (2.2), ma il fatto che possiamo quantificare la nostra incertezza: la posterior ci dice chiaramente quali valori di \(\lambda\) sono più o meno plausibili.

57.2.3 Diagnostica essenziale

Dopo il campionamento MCMC, è fondamentale verificare che le catene abbiano esplorato bene la distribuzione a posteriori. Per questo guardiamo due tipi di informazioni: indici numerici (come \(\hat{R}\) e ESS) e grafici delle catene (traceplot).

Possiamo ottenere una sintesi numerica della distribuzione a posteriore dei parametri con la funzione summarise_draws del pacchetto posterior:

posterior::summarise_draws(fit$draws("lambda"), rhat, ess_bulk, ess_tail)
#> # A tibble: 1 × 4
#>   variable  rhat ess_bulk ess_tail
#>   <chr>    <dbl>    <dbl>    <dbl>
#> 1 lambda    1.00  4666.65  6265.97
  • \(\hat{R}\) (R-hat): misura la convergenza delle catene. Valori vicini a 1.00 indicano che le catene si sono mescolate bene; valori maggiori di 1.01 segnalano possibili problemi.

  • ESS (Effective Sample Size): indica quanti campioni “indipendenti” equivalenti abbiamo ottenuto.

    • ess_bulk valuta la precisione delle stime centrali (media, mediana).
    • ess_tail valuta la precisione nelle code della distribuzione (intervalli credibili). Più sono grandi, meglio è: in genere migliaia di campioni equivalenti sono più che sufficienti.

Possiamo generare un traceplot con la funzione mcmc_trace di bayesplot:

bayesplot::mcmc_trace(fit$draws("lambda")) +
  ggtitle("Traceplot di λ")

Il traceplot mostra l’andamento dei valori di \(\lambda\) campionati dalle diverse catene. Se le catene:

  • oscillano liberamente attorno alla stessa regione,
  • senza trend sistematici o salti strani,

allora possiamo concludere che il campionamento è avvenuto correttamente.

In sintesi: se \(\hat{R} \approx 1\), ESS è elevato e i traceplot sono ben mescolati, possiamo fidarci delle stime ottenute.

57.3 Sparatorie mortali

Nella sezione precedente abbiamo esaminato il processo di derivazione della distribuzione a posteriori per i parametri della distribuzione Gamma, la quale viene impiegata quando si adotta un prior Gamma per una verosimiglianza di Poisson. In questo esempio, useremo tale metodo per affrontare una questione relativa all’analisi di un set di dati reali.

57.3.1 Domanda della ricerca

Come spiegato qui, i dati che esamineremo sono raccolti dal Washington Post con lo scopo di registrare ogni sparatoria mortale negli Stati Uniti ad opera di agenti di polizia, a partire dal 1° gennaio 2015. Il Washington Post ha adottato un approccio sistematico e accurato nella raccolta di queste informazioni, fornendo dati che possono essere utili per valutare i problemi legati alla violenza delle forze di polizia negli Stati Uniti.

Obiettivo. Stimare, per il periodo 2015–ultimo anno completo disponibile, il tasso medio annuo e l’incertezza associata. Poiché il 2025 è incompleto, lo escludiamo.

57.3.2 Importazione e pre-processing dei dati

# URL del dataset
url <- "https://raw.githubusercontent.com/washingtonpost/data-police-shootings/master/v2/fatal-police-shootings-data.csv"

# Importa i dati
fps_dat <- read_csv(url, show_col_types = FALSE)

# Conversione colonna date
fps_dat <- fps_dat %>%
  mutate(date = ymd(date),
         year = year(date))

# Esamina le colonne disponibili
colnames(fps_dat)
#>  [1] "id"                         "date"                      
#>  [3] "threat_type"                "flee_status"               
#>  [5] "armed_with"                 "city"                      
#>  [7] "county"                     "state"                     
#>  [9] "latitude"                   "longitude"                 
#> [11] "location_precision"         "name"                      
#> [13] "age"                        "gender"                    
#> [15] "race"                       "race_source"               
#> [17] "was_mental_illness_related" "body_camera"               
#> [19] "agency_ids"                 "year"

# Filtra eliminando i casi con year == 2025
fps <- fps_dat %>%
  filter(year != 2025)

# Conta le occorrenze per anno
year_counts <- fps %>%
  count(year, name = "events")

# Mostra i risultati
print(year_counts)
#> # A tibble: 10 × 2
#>     year events
#>    <dbl>  <int>
#>  1  2015    995
#>  2  2016    959
#>  3  2017    984
#>  4  2018    992
#>  5  2019    993
#>  6  2020   1021
#>  7  2021   1050
#>  8  2022   1097
#>  9  2023   1164
#> 10  2024   1175

57.3.3 Modello di Poisson (pooling completo)

Assumiamo \(y_t \sim \text{Poisson}(\lambda)\) con \(\lambda\) costante sul periodo:

\[ y_t \,\sim\, \text{Poisson}(\lambda), \quad t=1,\dots,n. \]

Il supporto di \(\lambda\) è \([0,\infty)\). Si noti che abbiamo considerato i dati come iid. Guardando la serie temporale, però, è ovvio che le cose non stanno così: i valori aumentano nel tempo.

57.3.4 Prior

Usiamo una prior coniugata Gamma su \(\lambda\), scelta in modo debolmente informativo. Un’ipotesi ragionevole (da verificare e discutere in aula) è una media a priori di 600 eventi/anno, con deviazione standard 200. In termini Gamma(shape, rate):

\[ \alpha = (\mu/\sigma)^2,\qquad \beta = \mu/\sigma^2. \]

Visualizziamo la prior (campionando in R):

mu    <- 600
sigma <- 200

# Parametrizzazione Gamma(shape = k, scale = theta) per la simulazione
theta <- sigma^2 / mu
k     <- mu / theta

set.seed(2)
x_draws <- rgamma(50000, shape = k, scale = theta)

ggplot(data.frame(x = x_draws), aes(x = x)) +
  geom_histogram(bins = 30, color = "white") +
  labs(
    x = "Tasso (eventi/anno)",
    y = "Frequenza",
    title = "Prior Gamma su λ (mu = 600, sigma = 200)"
  )

57.3.5 Modello di Poisson con Stan

Qui stimiamo \(\lambda\) assumendo lo stesso tasso per tutti gli anni (pooling completo). Con prior Gamma in parametrizzazione (shape, rate):

stan_code <- "
data {
  int<lower=1> N;                 // numero di anni
  array[N] int<lower=0> y;        // conteggi annuali
  real<lower=0> alpha_prior;      // shape
  real<lower=0> beta_prior;       // rate
}
parameters {
  real<lower=0> lambda;           // tasso medio annuo
}
model {
  lambda ~ gamma(alpha_prior, beta_prior); // prior Gamma(shape, rate)
  y ~ poisson(lambda);                     // verosimiglianza
}
generated quantities {
  real log_lik = poisson_lpmf(y | lambda);
}
"
# Dati (usa i conteggi 2015...2024 ordinati)
y_vec <- year_counts$events  # ordine per anno; per Poisson i.i.d. l'ordine non incide

# Prior: coerente con la sezione precedente
alpha_prior <- (mu / sigma)^2
beta_prior  <- mu / sigma^2

stan_data <- list(
  N = length(y_vec),
  y = as.integer(y_vec),
  alpha_prior = alpha_prior,
  beta_prior  = beta_prior
)
stan_data
#> $N
#> [1] 10
#> 
#> $y
#>  [1]  995  959  984  992  993 1021 1050 1097 1164 1175
#> 
#> $alpha_prior
#> [1] 9
#> 
#> $beta_prior
#> [1] 0.015

Compilazione ed esecuzione:

mod <- cmdstan_model(write_stan_file(stan_code))
fit <- mod$sample(
  data = stan_data,
  iter_warmup = 1000,
  iter_sampling = 4000,
  chains = 4,
  seed = 123,
  refresh = 0
)

Riassunto dei parametri:

fit$summary("lambda")
#> # A tibble: 1 × 10
#>   variable    mean  median    sd   mad      q5     q95  rhat ess_bulk
#>   <chr>      <dbl>   <dbl> <dbl> <dbl>   <dbl>   <dbl> <dbl>    <dbl>
#> 1 lambda   1042.41 1042.35 10.27 10.36 1025.69 1059.53  1.00  6110.78
#>   ess_tail
#>      <dbl>
#> 1  8233.18
posterior::summarise_draws(
  fit$draws("lambda"),
  mean, sd, ~quantile(.x, c(0.025, 0.5, 0.975))
)
#> # A tibble: 1 × 6
#>   variable    mean    sd  `2.5%`   `50%` `97.5%`
#>   <chr>      <dbl> <dbl>   <dbl>   <dbl>   <dbl>
#> 1 lambda   1042.41 10.27 1022.54 1042.35 1062.58

Visualizzazione:

bayesplot::mcmc_hist(fit$draws("lambda")) +
  ggtitle("Posterior di λ (tasso annuo)")

bayesplot::mcmc_areas(fit$draws("lambda"), prob = 0.95) +
  ggtitle("Posterior di λ (ICr 95%)")

Il grafico mostra che il tasso di sparatorie fatali ha una media annuale di 1042 con CI [1022, 1062].

In sintesi, analizzando i dati compresi tra il 2015 e il 2025 e basandoci su una distribuzione a priori che presuppone una sparatoria mortale al mese per stato, possiamo concludere con un grado di certezza soggettivo del 95% che il tasso stimato di sparatorie fatali da parte della polizia negli Stati Uniti sia di 1028 casi all’anno, con un intervallo di credibilità compreso tra 1022 e 1062.

Diagnostica essenziale:

# Indicatori numerici chiave: Rhat ~ 1, ESS adeguati
posterior::summarize_draws(
  fit$draws(c("lambda")), "rhat", "ess_bulk", "ess_tail"
)
#> # A tibble: 1 × 4
#>   variable  rhat ess_bulk ess_tail
#>   <chr>    <dbl>    <dbl>    <dbl>
#> 1 lambda    1.00  6110.78  8233.18
# Traceplot di controllo su OR
bayesplot::mcmc_trace(fit$draws("lambda")) +
  ggtitle("Traceplot di lambda")

57.3.6 Derivazione analitica (Gamma–Poisson)

Con prior \(\lambda \sim \text{Gamma}(\alpha,\beta)\) e dati \(y_1,\dots,y_n\) i.i.d. Poisson(\(\lambda\)), il posteriore è:

\[ \lambda \mid \mathbf{y} \;\sim\; \text{Gamma}\!\left(\alpha + \sum_{t=1}^n y_t,\;\; \beta + n\right). \]

Quindi:

  • media posteriore \(\mathbb{E}[\lambda\mid y] = \dfrac{\alpha + \sum y_t}{\beta + n}\);
  • ICr 95% con i quantili Gamma al 2.5% e 97.5%.
# Dati e prior come nella sezione Stan
data_vec <- year_counts$events
n        <- length(data_vec)
sum_y    <- sum(data_vec)

mu    <- 600
sigma <- 200
alpha_prior <- (mu / sigma)^2
beta_prior  <- mu / sigma^2

# Posterior coniugato
alpha_post <- alpha_prior + sum_y
beta_post  <- beta_prior  + n

post_mean <- alpha_post / beta_post
ci95      <- qgamma(c(0.025, 0.975), shape = alpha_post, rate = beta_post)

cat("Posterior mean λ:", round(post_mean, 2), "\n")
#> Posterior mean λ: 1042
cat("95% CrI: [", round(ci95[1], 2), ", ", round(ci95[2], 2), "]\n")
#> 95% CrI: [ 1022 ,  1062 ]

La derivazione analitica e i risultati MCMC coincidono (entro l’errore Monte Carlo).

57.4 Riflessioni conclusive

In questo capitolo abbiamo fatto un passo importante: abbiamo visto come tradurre un modello probabilistico relativamente semplice in Stan, come eseguire il campionamento MCMC, e come confrontare i risultati con la soluzione analitica. Questo esempio ci ha permesso di verificare che Stan funziona correttamente, perché in questo caso conoscevamo già la distribuzione a posteriori esatta.

L’aspetto davvero interessante, però, è che la stessa procedura può essere usata anche quando la soluzione analitica non esiste. Nei capitoli successivi incontreremo modelli più complessi, in cui la coniugatezza non ci aiuta più e non abbiamo formule chiuse per la distribuzione a posteriori. In quei casi, il campionamento MCMC sarà lo strumento indispensabile per fare inferenza bayesiana.

Qui abbiamo lavorato con un modello coniugato, in cui la posterior è ancora Gamma. Ma nei modelli più complessi che incontreremo (per esempio con predittori, effetti gerarchici o dinamici), non avremo più formule chiuse: sarà Stan, con MCMC, a permetterci di fare inferenza.

  • Come tradurre un modello teorico di conteggio (Poisson con prior Gamma) in un modello Stan.
  • Come ottenere campioni dalla distribuzione a posteriori con il campionamento MCMC.
  • Come confrontare i risultati con la soluzione analitica per verificare che il workflow funziona.
  • Perché è utile partire da un caso semplice: per allenarsi con il workflow che useremo in modelli più complessi, dove formule chiuse non esistono.
sessionInfo()
#> R version 4.5.1 (2025-06-13)
#> Platform: aarch64-apple-darwin20
#> Running under: macOS Sequoia 15.6.1
#> 
#> Matrix products: default
#> BLAS:   /Library/Frameworks/R.framework/Versions/4.5-arm64/Resources/lib/libRblas.0.dylib 
#> LAPACK: /Library/Frameworks/R.framework/Versions/4.5-arm64/Resources/lib/libRlapack.dylib;  LAPACK version 3.12.1
#> 
#> locale:
#> [1] C/UTF-8/C/C/C/C
#> 
#> time zone: Europe/Zagreb
#> tzcode source: internal
#> 
#> attached base packages:
#> [1] stats     graphics  grDevices utils     datasets  methods   base     
#> 
#> other attached packages:
#>  [1] lubridate_1.9.4       forcats_1.0.0         stringr_1.5.1        
#>  [4] purrr_1.1.0           readr_2.1.5           tidyverse_2.0.0      
#>  [7] cmdstanr_0.9.0        pillar_1.11.0         tinytable_0.11.0     
#> [10] patchwork_1.3.1       ggdist_3.3.3          tidybayes_3.0.7      
#> [13] bayesplot_1.13.0      ggplot2_3.5.2         reliabilitydiag_0.2.1
#> [16] priorsense_1.1.0      posterior_1.6.1       loo_2.8.0            
#> [19] rstan_2.32.7          StanHeaders_2.32.10   brms_2.22.0          
#> [22] Rcpp_1.1.0            sessioninfo_1.2.3     conflicted_1.2.0     
#> [25] janitor_2.2.1         matrixStats_1.5.0     modelr_0.1.11        
#> [28] tibble_3.3.0          dplyr_1.1.4           tidyr_1.3.1          
#> [31] rio_1.2.3             here_1.0.1           
#> 
#> loaded via a namespace (and not attached):
#>  [1] gridExtra_2.3        inline_0.3.21        sandwich_3.1-1      
#>  [4] rlang_1.1.6          magrittr_2.0.3       multcomp_1.4-28     
#>  [7] snakecase_0.11.1     ggridges_0.5.6       compiler_4.5.1      
#> [10] reshape2_1.4.4       systemfonts_1.2.3    vctrs_0.6.5         
#> [13] crayon_1.5.3         pkgconfig_2.0.3      arrayhelpers_1.1-0  
#> [16] fastmap_1.2.0        backports_1.5.0      labeling_0.4.3      
#> [19] utf8_1.2.6           rmarkdown_2.29       tzdb_0.5.0          
#> [22] ps_1.9.1             ragg_1.4.0           bit_4.6.0           
#> [25] xfun_0.52            cachem_1.1.0         jsonlite_2.0.0      
#> [28] broom_1.0.9          parallel_4.5.1       R6_2.6.1            
#> [31] stringi_1.8.7        RColorBrewer_1.1-3   estimability_1.5.1  
#> [34] knitr_1.50           zoo_1.8-14           pacman_0.5.1        
#> [37] Matrix_1.7-3         splines_4.5.1        timechange_0.3.0    
#> [40] tidyselect_1.2.1     abind_1.4-8          yaml_2.3.10         
#> [43] codetools_0.2-20     curl_6.4.0           processx_3.8.6      
#> [46] pkgbuild_1.4.8       plyr_1.8.9           lattice_0.22-7      
#> [49] withr_3.0.2          bridgesampling_1.1-2 coda_0.19-4.1       
#> [52] evaluate_1.0.4       survival_3.8-3       RcppParallel_5.1.10 
#> [55] tensorA_0.36.2.1     checkmate_2.3.2      stats4_4.5.1        
#> [58] distributional_0.5.0 generics_0.1.4       vroom_1.6.5         
#> [61] rprojroot_2.1.0      hms_1.1.3            rstantools_2.4.0    
#> [64] scales_1.4.0         xtable_1.8-4         glue_1.8.0          
#> [67] emmeans_1.11.2       tools_4.5.1          data.table_1.17.8   
#> [70] mvtnorm_1.3-3        grid_4.5.1           QuickJSR_1.8.0      
#> [73] colorspace_2.1-1     nlme_3.1-168         cli_3.6.5           
#> [76] textshaping_1.0.1    svUnit_1.0.6         Brobdingnag_1.2-9   
#> [79] V8_6.0.5             gtable_0.3.6         digest_0.6.37       
#> [82] TH.data_1.1-3        htmlwidgets_1.6.4    farver_2.1.2        
#> [85] memoise_2.0.1        htmltools_0.5.8.1    lifecycle_1.0.4     
#> [88] bit64_4.6.0-1        MASS_7.3-65

Bibliografia