Quase-verossimilhança e Modelo Binomial Negativo com aplicações em R
Autor
Prof. Dr. Dennison Carvalho - Baseado em Dobson & Barnett (2018)
Data de Publicação
26 de junho de 2026
Sobre esta apostila. O modelo de Poisson assume que média e variância são iguais. Na prática, dados de contagem quase sempre violam essa suposição — fenômeno chamado superdispersão. Esta apostila apresenta o diagnóstico de superdispersão e duas soluções práticas: o quasi-Poisson e a Binomial Negativa, aplicadas ao banco de dados quine do pacote MASS.
1 Os Dados: Faltas Escolares (quine)
O banco quine contém dados de 146 crianças australianas do ensino fundamental. Registra o número de dias de ausência escolar (Days) e quatro variáveis explicativas categóricas.
Variável
Tipo
Descrição
Days
Resposta (contagem)
Dias ausentes no ano letivo
Eth
Fator (A/N)
Etnia: A = aborígine, N = não-aborígine
Sex
Fator (F/M)
Sexo: feminino / masculino
Age
Fator (F0/F1/F2/F3)
Faixa etária / série escolar
Lrn
Fator (AL/SL)
Ritmo de aprendizagem: AL = médio, SL = lento
Código
library(MASS) # para o banco quine e para glm.nb()library(ggplot2) # para os gráficosdata(quine)# Visão geral do bancostr(quine)
cat(sprintf("\nVar / Média = %.2f\n", var(quine$Days) /mean(quine$Days)))
Var / Média = 16.05
Código
cat("(Poisson exige Var/Média ≈ 1)\n")
(Poisson exige Var/Média ≈ 1)
Código
# Frequências por grupocat("\n=== Frequências por variável ===\n\n")
=== Frequências por variável ===
Código
cat("Etnia:\n"); print(table(quine$Eth))
Etnia:
A N
69 77
Código
cat("\nSexo:\n"); print(table(quine$Sex))
Sexo:
F M
80 66
Código
cat("\nSérie:\n"); print(table(quine$Age))
Série:
F0 F1 F2 F3
27 46 40 33
Código
cat("\nRitmo:\n"); print(table(quine$Lrn))
Ritmo:
AL SL
83 63
Código
ggplot(quine, aes(x = Days)) +geom_histogram(binwidth =5, fill ="#4C72B0",color ="white", alpha =0.85) +geom_vline(xintercept =mean(quine$Days),color ="#C44E52", linewidth =1.2, linetype ="dashed") +annotate("text", x =mean(quine$Days) +3, y =28,label =paste0("média = ", round(mean(quine$Days), 1)),color ="#C44E52", size =4) +labs(title ="Distribuição dos dias ausentes — banco quine",subtitle ="Cauda longa e variância muito maior que a média indicam superdispersão",x ="Dias ausentes (Days)", y ="Frequência") +theme_minimal(base_size =12) +theme(plot.title =element_text(face ="bold"))
Distribuição dos dias ausentes. Cauda longa à direita e presença de zeros são típicos de superdispersão.
Código
ggplot(quine, aes(x = Age, y = Days, fill = Lrn)) +geom_boxplot(alpha =0.7, outlier.shape =16) +facet_wrap(~ Eth + Sex, labeller = label_both) +scale_fill_manual(values =c("#4C72B0","#C44E52"),name ="Ritmo\n(AL=médio,SL=lento)") +labs(title ="Dias ausentes por série, ritmo, etnia e sexo",subtitle ="Alta variabilidade dentro dos grupos confirma superdispersão",x ="Série (Age)", y ="Dias ausentes") +theme_minimal(base_size =11) +theme(plot.title =element_text(face ="bold"))
Dias ausentes por grupo. A dispersão dentro de cada grupo é grande, especialmente em crianças com ritmo lento (SL) e nas séries mais avançadas.
O que os gráficos mostram: a variância/média \(\approx 20\) (muito acima de 1) e a cauda longa à direita indicam superdispersão forte. O modelo de Poisson padrão não é adequado para esses dados.
2 Revisão: O Problema da Superdispersão
2.1 O modelo de Poisson assume
\[\text{var}(Y_i) = E(Y_i) = \lambda_i\]
Na superdispersão, a variância real é maior que a média:
Consequência prática: ignorar a superdispersão produz erros padrão subestimados por um fator \(\sqrt{\hat\phi}\), tornando os testes \(z\) e \(p\)-valores incorretamente otimistas.
ou equivalentemente via deviance: \(\hat\phi = D/(N-p)\).
2.3 As duas soluções
Quasi-Poisson: mantém os coeficientes do Poisson e multiplica os erros padrão por \(\sqrt{\hat\phi}\). Não especifica uma distribuição completa — apenas a relação \(\text{var}(Y) = \phi\,\mu\).
Binomial Negativa: especifica uma distribuição completa com parâmetro extra \(\theta\):
Quanto menor \(\theta\), maior a superdispersão. Quando \(\theta \to \infty\), converge para Poisson.
3 Passo 1 — Modelo de Poisson (ponto de partida)
Atenção
Note que os erros-padrão são todos muito próximos de zero. Na prática, ao observarmos dados reais, este detalhe já acende um alerta para possível superdirpersão, o que consequentemente pode estar levando os valores-p erroneamente a serem extremamente significativos.
Código
# Ajuste do modelo de Poisson padrãomod_pois <-glm(Days ~ Eth + Sex + Age + Lrn,family =poisson(link ="log"),data = quine)summary(mod_pois)
Call:
glm(formula = Days ~ Eth + Sex + Age + Lrn, family = poisson(link = "log"),
data = quine)
Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) 2.71538 0.06468 41.980 < 2e-16 ***
EthN -0.53360 0.04188 -12.740 < 2e-16 ***
SexM 0.16160 0.04253 3.799 0.000145 ***
AgeF1 -0.33390 0.07009 -4.764 1.90e-06 ***
AgeF2 0.25783 0.06242 4.131 3.62e-05 ***
AgeF3 0.42769 0.06769 6.319 2.64e-10 ***
LrnSL 0.34894 0.05204 6.705 2.02e-11 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
(Dispersion parameter for poisson family taken to be 1)
Null deviance: 2073.5 on 145 degrees of freedom
Residual deviance: 1696.7 on 139 degrees of freedom
AIC: 2299.2
Number of Fisher Scoring iterations: 5
3.1 Calculando \(\hat\phi\) (diagnóstico)
Código
# Estatísticas de dispersãoX2 <-sum(residuals(mod_pois, type ="pearson")^2)gl <-df.residual(mod_pois)phi <- X2 / glcat("=== Diagnóstico de superdispersão ===\n\n")
=== Diagnóstico de superdispersão ===
Código
cat(sprintf("X² de Pearson = %.2f\n", X2))
X² de Pearson = 1830.19
Código
cat(sprintf("Graus de liberdade = %d\n", gl))
Graus de liberdade = 139
Código
cat(sprintf("phi_hat = X²/gl = %.2f\n\n", phi))
phi_hat = X²/gl = 13.17
Código
cat(sprintf("Os erros padrão do Poisson estão subestimados\n"))
Os erros padrão do Poisson estão subestimados
Código
cat(sprintf("por um fator de sqrt(%.2f) = %.2f\n", phi, sqrt(phi)))
por um fator de sqrt(13.17) = 3.63
Diagnóstico
\(\hat\phi = 13,17 \gg 1\). Isso significa que os erros padrão do Poisson estão subestimados por um fator de \(\sqrt{\hat\phi} \approx 3,6\). Os valores-\(p\) estão incorretos — precisamos corrigir.
3.2 Gráficos de diagnóstico do Poisson
Código
par(mfrow =c(1, 2), mar =c(4, 4, 3, 1))# Gráfico 1: resíduos de Pearson vs. valores ajustadosplot(log(fitted(mod_pois)),residuals(mod_pois, type ="pearson"),xlab ="log(valores ajustados)",ylab ="Resíduos de Pearson",main ="Resíduos vs. Ajustados\n(Poisson)",pch =16, col ="#4C72B088")abline(h =c(-2, 0, 2), lty =c(2, 1, 2),col =c("red","gray50","red"))# Gráfico 2: QQ-plot dos resíduos de devianceqqnorm(residuals(mod_pois, type ="deviance"),main ="QQ-plot — Resíduos Poisson",pch =16, col ="#4C72B088")qqline(residuals(mod_pois, type ="deviance"),col ="red", lwd =1.5)
Diagnóstico do modelo Poisson. Esquerda: padrão em leque nos resíduos — variância cresce muito mais que a média, confirmando superdispersão. Direita: QQ-plot com caudas pesadas fora da reta.
Código
par(mfrow =c(1, 1))
Leitura dos gráficos
Esquerda (padrão em leque): os resíduos se espalham cada vez mais à medida que os valores ajustados crescem. Isso é a assinatura visual da superdispersão — a variância real cresce mais rápido do que o modelo Poisson prevê.
Direita (QQ-plot): os pontos nas extremidades saem muito da reta diagonal, especialmente no extremo superior. Isso indica caudas mais pesadas do que a distribuição normal, consistente com superdispersão forte.
4 Passo 2 — Quasi-Poisson
O quasi-Poisson é a solução mais simples: muda apenas family = quasipoisson. Os coeficientes são idênticos ao Poisson, mas os erros padrão são corrigidos.
Código
# Modelo quasi-Poisson — mudar apenas a familymod_quasi <-glm(Days ~ Eth + Sex + Age + Lrn,family =quasipoisson(link ="log"),data = quine)summary(mod_quasi)
Call:
glm(formula = Days ~ Eth + Sex + Age + Lrn, family = quasipoisson(link = "log"),
data = quine)
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 2.7154 0.2347 11.569 < 2e-16 ***
EthN -0.5336 0.1520 -3.511 0.000602 ***
SexM 0.1616 0.1543 1.047 0.296914
AgeF1 -0.3339 0.2543 -1.313 0.191413
AgeF2 0.2578 0.2265 1.138 0.256938
AgeF3 0.4277 0.2456 1.741 0.083831 .
LrnSL 0.3489 0.1888 1.848 0.066760 .
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
(Dispersion parameter for quasipoisson family taken to be 13.16691)
Null deviance: 2073.5 on 145 degrees of freedom
Residual deviance: 1696.7 on 139 degrees of freedom
AIC: NA
Number of Fisher Scoring iterations: 5
4.1 Comparando erros padrão
Código
# Extrair erros padrão dos dois modelosep_pois <-coef(summary(mod_pois))[, "Std. Error"]ep_quasi <-coef(summary(mod_quasi))[, "Std. Error"]cat("=== Erros padrão: Poisson vs. Quasi-Poisson ===\n\n")
for (mu_ex inc(5, 10, 20, 50)) {cat(sprintf(" mu = %2d => var = %.1f (Poisson teria var = %d)\n", mu_ex, mu_ex + mu_ex^2/theta, mu_ex))}
mu = 5 => var = 24.6 (Poisson teria var = 5)
mu = 10 => var = 88.4 (Poisson teria var = 10)
mu = 20 => var = 333.8 (Poisson teria var = 20)
mu = 50 => var = 2010.9 (Poisson teria var = 50)
6 Passo 4 — Comparação dos Três Modelos
6.1 Coeficientes e erros padrão
Código
# Reunir coeficientes dos três modelos em uma tabela simplesb_p <-coef(mod_pois)b_q <-coef(mod_quasi)b_nb <-coef(mod_nb)ep_p <-coef(summary(mod_pois))[, 2]ep_q <-coef(summary(mod_quasi))[, 2]ep_nb <-coef(summary(mod_nb))[, 2]cat("=== Coeficientes e erros padrão — três modelos ===\n\n")
=== Coeficientes e erros padrão — três modelos ===
# Teste de razão de verossimilhança BN vs Poissonlrt_stat <-2*(as.numeric(logLik(mod_nb)) -as.numeric(logLik(mod_pois)))p_lrt <-pchisq(lrt_stat, df =1, lower.tail =FALSE)cat(sprintf("Teste LRT (BN vs Poisson): stat = %.2f, p = %.2e\n", lrt_stat, p_lrt))
Teste LRT (BN vs Poisson): stat = 1192.03, p = 3.29e-261
Código
cat("=> BN é SIGNIFICATIVAMENTE melhor que Poisson\n\n")
=> BN é SIGNIFICATIVAMENTE melhor que Poisson
Código
cat("MODELO ESCOLHIDO: Binomial Negativa\n")
MODELO ESCOLHIDO: Binomial Negativa
Código
cat("(mas quasi-Poisson também é adequado quando AIC não é necessário)\n")
(mas quasi-Poisson também é adequado quando AIC não é necessário)
IRR significa Incidence Rate Ratio — em português, Razão de Taxas de Incidência.
\[IRR = \dfrac{\text{taxa esperada do grupo 1}}{\text{taxa esperada do grupo referência}}\]
A RC é usada no modelo Binomial (resposta binária) e a IRR nos modelos Poisson, Binomial Negativa (BN) e Quasi-Poisson (QP), em que as respostas são contagens.
O IRR na Poisson, BN e QP compara taxas médias de eventos. O RC na Binomial compara as chances (odds) de sucesso.
Resumo: RC compara chances de um evento binário; IRR compara taxas médias de contagem. A fórmula \(e^{\hat{\beta}}\) é a mesma, mas os denominadores são completamente diferentes.
7.3 Interpretação de cada coeficiente
Importante
Somente os coeficientes \(\beta_{0}\) (intercepto) \(\beta_{1}\) (etnia não aborígine) foram significativos.
Normalmente, rodaríamos o modelo novamente apenas com a covariável que apresenta significância estatística. Todavia, para fins didáticos, faremos as análises de todas covariáveis.
Código
cat("=== INTERPRETAÇÃO DOS COEFICIENTES (modelo BN) ===\n\n")
=== INTERPRETAÇÃO DOS COEFICIENTES (modelo BN) ===
Código
cat("INTERCEPTO:\n")
INTERCEPTO:
Código
cat(sprintf(" Criança de referência: etnia N, sexo F, série F0, ritmo AL\n"))
Criança de referência: etnia N, sexo F, série F0, ritmo AL
Código
cat(sprintf(" Número esperado de faltas = exp(%.4f) = %.1f dias\n\n", cf["(Intercept)"], exp(cf["(Intercept)"])))
Número esperado de faltas = exp(2.8946) = 18.1 dias
Código
cat("ETNIA (EthN = não-aborígine vs. aborígine):\n")
cat(sprintf(" Meninos faltam %.0f%% %s que meninas\n",abs(exp(cf["SexM"])-1)*100,ifelse(exp(cf["SexM"])>1,"mais","menos")))
Meninos faltam 9% mais que meninas
Código
cat(sprintf(" p = %.4f %s\n\n", p_val["SexM"],ifelse(p_val["SexM"]<0.05,"(significativo)","(não significativo)")))
p = 0.6067 (não significativo)
Código
cat("SÉRIE (referência = F0):\n")
SÉRIE (referência = F0):
Código
series <-c("AgeF1","AgeF2","AgeF3")for (s in series) {if (s %in%names(cf)) {cat(sprintf(" %s: b = %.4f => IRR = %.4f (%.0f%% %s) p = %.4f\n", s, cf[s], exp(cf[s]),abs(exp(cf[s])-1)*100,ifelse(exp(cf[s])>1,"mais","menos"), p_val[s])) }}
AgeF1: b = -0.4484 => IRR = 0.6386 (36% menos) p = 0.0614
AgeF2: b = 0.0881 => IRR = 1.0921 (9% mais) p = 0.7092
AgeF3: b = 0.3569 => IRR = 1.4289 (43% mais) p = 0.1507
Código
cat("\nRITMO DE APRENDIZAGEM (LrnSL = lento vs. AL = médio):\n")
RITMO DE APRENDIZAGEM (LrnSL = lento vs. AL = médio):
cat(sprintf(" Crianças com ritmo lento faltam %.0f%% %s\n",abs(exp(cf["LrnSL"])-1)*100,ifelse(exp(cf["LrnSL"])>1,"mais","menos")))
Crianças com ritmo lento faltam 34% mais
Código
cat(sprintf(" p = %.4f %s\n", p_val["LrnSL"],ifelse(p_val["LrnSL"]<0.05,"(significativo)","(não significativo)")))
p = 0.1172 (não significativo)
8 Análise Gráfica dos Resíduos
8.1 Diagnóstico completo do modelo BN
Código
par(mfrow =c(1, 2), mar =c(4, 4, 3, 1))# Gráfico 1: resíduos vs. ajustadosplot(log(fitted(mod_nb)),residuals(mod_nb, type ="pearson"),xlab ="log(valores ajustados)",ylab ="Resíduos de Pearson",main ="Resíduos vs. Ajustados\n(Binomial Negativa)",pch =16, col ="#2CA02C88")abline(h =c(-2, 0, 2), lty =c(2, 1, 2),col =c("red","gray50","red"))# Gráfico 2: QQ-plotqqnorm(residuals(mod_nb, type ="deviance"),main ="QQ-plot — Resíduos BN",pch =16, col ="#2CA02C88")qqline(residuals(mod_nb, type ="deviance"),col ="red", lwd =1.5)
Diagnóstico do modelo Binomial Negativa. O padrão em leque desapareceu (esquerda) e o QQ-plot mostra resíduos mais próximos da normalidade (direita) — a BN capturou bem a superdispersão.
Código
par(mfrow =c(1, 1))
8.2 Comparando o antes e o depois
Dica
Note que o padrão em leque (resíduos espalhando cada vez mais à medida que \(\log (\hat{\mu})\) cresce — é exatamente a assinatura visual da superdispersão) some e a distribuição fica mais simétrica com a BN.
Código
par(mfrow =c(2, 2), mar =c(4, 4, 3, 1))# Poisson: resíduos vs. ajustadosplot(log(fitted(mod_pois)),residuals(mod_pois, type ="pearson"),xlab ="log(ajustados)", ylab ="Resíduo Pearson",main ="Poisson: resíduos vs. ajustados",pch =16, col ="#C44E5288")abline(h =c(-2,0,2), lty=c(2,1,2), col=c("red","gray50","red"))# Poisson: QQqqnorm(residuals(mod_pois, type ="deviance"),main ="Poisson: QQ-plot", pch =16, col ="#C44E5288")qqline(residuals(mod_pois, type ="deviance"),col ="red", lwd =1.5)# BN: resíduos vs. ajustadosplot(log(fitted(mod_nb)),residuals(mod_nb, type ="pearson"),xlab ="log(ajustados)", ylab ="Resíduo Pearson",main ="Bin. Negativa: resíduos vs. ajustados",pch =16, col ="#2CA02C88")abline(h =c(-2,0,2), lty=c(2,1,2), col=c("red","gray50","red"))# BN: QQqqnorm(residuals(mod_nb, type ="deviance"),main ="Bin. Negativa: QQ-plot", pch =16, col ="#2CA02C88")qqline(residuals(mod_nb, type ="deviance"),col ="red", lwd =1.5)
Comparação visual dos resíduos de Pearson: Poisson (vermelho, topo) vs. Binomial Negativa (verde, baixo). .
Código
par(mfrow =c(1, 1))
Importante
No script da apostila foi usado Pearson no gráfico resíduos vs. ajustados porque é o mais intuitivo para mostrar o padrão em leque da superdispersão — a relação direta com \(V(\hat{\mu_{i}})\) torna o leque visualmente óbvio. Mas o correto seria usar os dois, como foi feito no QQ-plot (que usou deviance). O ideal para as análises seria:
Resíduos vs. log(ajustados): usar deviance (diagnóstico geral).
QQ-plot: usar deviance (verificar normalidade).
Detecção do padrão em leque: mostrar Pearson como complemento.
8.3 Verificação da dispersão após a BN
Código
cat("=== Verificação da dispersão residual ===\n\n")
=== Verificação da dispersão residual ===
Código
phi_res_nb <-sum(residuals(mod_nb, type="pearson")^2) /df.residual(mod_nb)cat(sprintf("Poisson: phi_hat = %.2f (muito longe de 1 — RUIM)\n", phi))
Poisson: phi_hat = 13.17 (muito longe de 1 — RUIM)
Código
cat(sprintf("BN: phi_res = %.2f (próximo de 1 — BOM)\n\n", phi_res_nb))
BN: phi_res = 0.99 (próximo de 1 — BOM)
Código
cat("Interpretar:\n")
Interpretar:
Código
cat(" phi_res ≈ 1 significa que a BN capturou toda\n")