Aula 5 — ANOVA, Diagnóstico de Pressupostos, Kruskal-Wallis e GLM de Poisson

1 Apresentação

Esta apostila organiza, comenta e expande o exemplo da base InsectSprays, com foco em comparação de médias para dados de contagem. O objetivo é mostrar, de forma sequencial, como:

  1. explorar os dados;
  2. ajustar uma ANOVA de um fator;
  3. verificar pressupostos do modelo;
  4. considerar alternativas quando os pressupostos não são bem atendidos;
  5. realizar comparações múltiplas;
  6. ajustar um modelo linear generalizado para contagens.

Ao longo do texto, as fórmulas estatísticas são apresentadas para conectar a prática computacional aos fundamentos inferenciais.

2 Carregamento dos dados

Usaremos a base InsectSprays, já disponível no R. Ela registra a contagem de insetos sobreviventes (count) em parcelas tratadas com diferentes pulverizações (spray).

library(tidyverse)
── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.2.0     ✔ readr     2.2.0
✔ forcats   1.0.1     ✔ stringr   1.6.0
✔ ggplot2   4.0.2     ✔ tibble    3.3.1
✔ lubridate 1.9.5     ✔ tidyr     1.3.2
✔ purrr     1.2.1     
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
insetos <- InsectSprays

glimpse(insetos)
Rows: 72
Columns: 2
$ count <dbl> 10, 7, 20, 14, 14, 12, 10, 23, 17, 20, 14, 13, 11, 17, 21, 11, 1…
$ spray <fct> A, A, A, A, A, A, A, A, A, A, A, A, B, B, B, B, B, B, B, B, B, B…
head(insetos)
  count spray
1    10     A
2     7     A
3    20     A
4    14     A
5    14     A
6    12     A

3 Exploração inicial dos dados

Antes de ajustar qualquer modelo, é importante visualizar a distribuição das respostas em cada tratamento. Isso permite observar diferenças aparentes entre grupos, dispersão, assimetria e possíveis valores extremos.

insetos |>
  ggplot(aes(x = spray, y = count)) +
  geom_boxplot(outlier.colour = NA) +
  geom_jitter(width = 0.1, alpha = 0.7) +
  theme_classic() +
  labs(
    x = "Tratamento (spray)",
    y = "Número de insetos sobreviventes"
  )

Também é útil obter estatísticas descritivas por grupo.

insetos |>
  group_by(spray) |>
  summarise(
    n = n(),
    media = mean(count),
    desvio_padrao = sd(count),
    erro_padrao = sd(count) / sqrt(n)
  )
# A tibble: 6 × 5
  spray     n media desvio_padrao erro_padrao
  <fct> <int> <dbl>         <dbl>       <dbl>
1 A        12 14.5           4.72       1.36 
2 B        12 15.3           4.27       1.23 
3 C        12  2.08          1.98       0.570
4 D        12  4.92          2.50       0.723
5 E        12  3.5           1.73       0.5  
6 F        12 16.7           6.21       1.79 

4 ANOVA de um fator

4.1 Ideia do modelo

A ANOVA de um fator testa a hipótese de que todas as médias populacionais dos grupos são iguais.

A hipótese nula é:

\[ H_0: \mu_A = \mu_B = \mu_C = \mu_D = \mu_E = \mu_F \]

A hipótese alternativa é:

\[ H_1: \text{pelo menos uma média difere das demais} \]

O modelo linear da ANOVA pode ser escrito como:

\[ Y_{ij} = \mu + \tau_i + \varepsilon_{ij} \]

em que:

  • \(Y_{ij}\) é a observação \(j\) no tratamento \(i\);
  • \(\mu\) é a média geral;
  • \(\tau_i\) é o efeito do tratamento \(i\);
  • \(\varepsilon_{ij}\) é o erro aleatório associado à observação.

Assume-se que:

\[ \varepsilon_{ij} \sim N(0, \sigma^2) \]

ou seja, os erros são independentes, normalmente distribuídos e com variância comum.

4.2 Decomposição da variabilidade

A ANOVA particiona a soma de quadrados total em duas partes:

\[ SQ_{Total} = SQ_{Tratamentos} + SQ_{Resíduo} \]

ou, de forma equivalente,

\[ SS_T = SS_{Trat} + SS_E \]

A estatística de teste é:

\[ F = \frac{QM_{Tratamentos}}{QM_{Resíduo}} \]

em que:

\[ QM_{Tratamentos} = \frac{SQ_{Tratamentos}}{g - 1} \]

\[ QM_{Resíduo} = \frac{SQ_{Resíduo}}{N - g} \]

com \(g\) representando o número de grupos e \(N\) o número total de observações.

Quando \(H_0\) é verdadeira, espera-se que \(F\) seja próximo de 1. Valores muito maiores que 1 indicam evidência de diferença entre médias.

4.3 Ajuste do modelo

m1 <- lm(count ~ spray, data = insetos)

m1

Call:
lm(formula = count ~ spray, data = insetos)

Coefficients:
(Intercept)       sprayB       sprayC       sprayD       sprayE       sprayF  
    14.5000       0.8333     -12.4167      -9.5833     -11.0000       2.1667  
summary(m1)

Call:
lm(formula = count ~ spray, data = insetos)

Residuals:
   Min     1Q Median     3Q    Max 
-8.333 -1.958 -0.500  1.667  9.333 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  14.5000     1.1322  12.807  < 2e-16 ***
sprayB        0.8333     1.6011   0.520    0.604    
sprayC      -12.4167     1.6011  -7.755 7.27e-11 ***
sprayD       -9.5833     1.6011  -5.985 9.82e-08 ***
sprayE      -11.0000     1.6011  -6.870 2.75e-09 ***
sprayF        2.1667     1.6011   1.353    0.181    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 3.922 on 66 degrees of freedom
Multiple R-squared:  0.7244,    Adjusted R-squared:  0.7036 
F-statistic:  34.7 on 5 and 66 DF,  p-value: < 2.2e-16
anova(m1)
Analysis of Variance Table

Response: count
          Df Sum Sq Mean Sq F value    Pr(>F)    
spray      5 2668.8  533.77  34.702 < 2.2e-16 ***
Residuals 66 1015.2   15.38                      
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

5 Diagnóstico dos pressupostos

A interpretação da ANOVA depende da avaliação dos resíduos do modelo. Em termos práticos, queremos verificar se os resíduos apresentam distribuição aproximadamente normal e variância aproximadamente constante entre tratamentos.

5.1 Resíduos do modelo

Os resíduos são calculados como:

\[ e_{ij} = y_{ij} - \hat{y}_{ij} \]

onde \(y_{ij}\) é o valor observado e \(\hat{y}_{ij}\) é o valor ajustado pelo modelo.

5.2 Histograma dos resíduos

hist(m1$residuals,
     main = "Histograma dos resíduos do modelo m1",
     xlab = "Resíduos")

5.3 Teste de normalidade de Shapiro-Wilk

O teste de Shapiro-Wilk avalia a hipótese:

\[ H_0: \text{os resíduos seguem distribuição normal} \]

shapiro.test(m1$residuals)

    Shapiro-Wilk normality test

data:  m1$residuals
W = 0.96006, p-value = 0.02226

5.4 Teste de homogeneidade de variâncias

O teste de Bartlett avalia:

\[ H_0: \sigma_A^2 = \sigma_B^2 = \cdots = \sigma_F^2 \]

bartlett.test(count ~ spray, data = insetos)

    Bartlett test of homogeneity of variances

data:  count by spray
Bartlett's K-squared = 25.96, df = 5, p-value = 9.085e-05

5.5 Funções auxiliares para diagnóstico

library(performance)
Warning: package 'performance' was built under R version 4.5.3
check_normality(m1)
Warning: Non-normality of residuals detected (p = 0.022).
check_heteroscedasticity(m1)
Warning: Heteroscedasticity (non-constant error variance) detected (p < .001).

5.6 Simulação de resíduos

O pacote DHARMa oferece uma visualização bastante útil do comportamento dos resíduos simulados.

library(DHARMa)
Warning: package 'DHARMa' was built under R version 4.5.3
This is DHARMa 0.4.7. For overview type '?DHARMa'. For recent changes, type news(package = 'DHARMa')
plot(simulateResiduals(m1))

6 Alternativa não paramétrica: teste de Kruskal-Wallis

Quando os pressupostos da ANOVA não são adequadamente atendidos, uma alternativa é o teste de Kruskal-Wallis. Esse teste compara grupos com base nos postos, e não nas médias originais.

A hipótese nula continua sendo a de ausência de diferença sistemática entre os grupos.

kruskal.test(count ~ spray, data = insetos)

    Kruskal-Wallis rank sum test

data:  count by spray
Kruskal-Wallis chi-squared = 54.691, df = 5, p-value = 1.511e-10

Também podemos usar uma função que já fornece agrupamentos de comparação.

library(agricolae)

k <- kruskal(insetos$count, insetos$spray)
k
$statistics
     Chisq Df      p.chisq  t.value      MSD
  54.69134  5 1.510845e-10 1.996564 8.462804

$parameters
            test p.ajusted        name.t ntr alpha
  Kruskal-Wallis      none insetos$spray   6  0.05

$means
  insetos.count     rank      std  r Min Max   Q25  Q50   Q75
A     14.500000 52.16667 4.719399 12   7  23 11.50 14.0 17.75
B     15.333333 54.83333 4.271115 12   7  21 12.50 16.5 17.50
C      2.083333 11.45833 1.975225 12   0   7  1.00  1.5  3.00
D      4.916667 25.58333 2.503028 12   2  12  3.75  5.0  5.00
E      3.500000 19.33333 1.732051 12   1   6  2.75  3.0  5.00
F     16.666667 55.62500 6.213378 12   9  26 12.50 15.0 22.50

$comparison
NULL

$groups
  insetos$count groups
F      55.62500      a
B      54.83333      a
A      52.16667      a
D      25.58333      b
E      19.33333     bc
C      11.45833      c

attr(,"class")
[1] "group"

7 Transformação da variável resposta

Para dados de contagem, uma transformação simples e clássica é a raiz quadrada:

\[ Y' = \sqrt{Y} \]

Essa transformação pode reduzir assimetria e estabilizar a variância em algumas situações.

Primeiro criamos uma nova coluna com a resposta transformada.

insetos2 <- insetos |>
  mutate(count_sqrt = sqrt(count))

insetos2
   count spray count_sqrt
1     10     A   3.162278
2      7     A   2.645751
3     20     A   4.472136
4     14     A   3.741657
5     14     A   3.741657
6     12     A   3.464102
7     10     A   3.162278
8     23     A   4.795832
9     17     A   4.123106
10    20     A   4.472136
11    14     A   3.741657
12    13     A   3.605551
13    11     B   3.316625
14    17     B   4.123106
15    21     B   4.582576
16    11     B   3.316625
17    16     B   4.000000
18    14     B   3.741657
19    17     B   4.123106
20    17     B   4.123106
21    19     B   4.358899
22    21     B   4.582576
23     7     B   2.645751
24    13     B   3.605551
25     0     C   0.000000
26     1     C   1.000000
27     7     C   2.645751
28     2     C   1.414214
29     3     C   1.732051
30     1     C   1.000000
31     2     C   1.414214
32     1     C   1.000000
33     3     C   1.732051
34     0     C   0.000000
35     1     C   1.000000
36     4     C   2.000000
37     3     D   1.732051
38     5     D   2.236068
39    12     D   3.464102
40     6     D   2.449490
41     4     D   2.000000
42     3     D   1.732051
43     5     D   2.236068
44     5     D   2.236068
45     5     D   2.236068
46     5     D   2.236068
47     2     D   1.414214
48     4     D   2.000000
49     3     E   1.732051
50     5     E   2.236068
51     3     E   1.732051
52     5     E   2.236068
53     3     E   1.732051
54     6     E   2.449490
55     1     E   1.000000
56     1     E   1.000000
57     3     E   1.732051
58     2     E   1.414214
59     6     E   2.449490
60     4     E   2.000000
61    11     F   3.316625
62     9     F   3.000000
63    15     F   3.872983
64    22     F   4.690416
65    15     F   3.872983
66    16     F   4.000000
67    13     F   3.605551
68    10     F   3.162278
69    26     F   5.099020
70    26     F   5.099020
71    24     F   4.898979
72    13     F   3.605551

7.1 Visualização após transformação

insetos2 |>
  ggplot(aes(x = spray, y = count_sqrt)) +
  geom_boxplot(outlier.colour = NA) +
  geom_jitter(width = 0.1, alpha = 0.7) +
  theme_classic() +
  labs(
    x = "Tratamento (spray)",
    y = expression(sqrt("Número de insetos sobreviventes"))
  )

7.2 Ajuste do modelo com dados transformados

m2 <- lm(count_sqrt ~ spray, data = insetos2)

m2

Call:
lm(formula = count_sqrt ~ spray, data = insetos2)

Coefficients:
(Intercept)       sprayB       sprayC       sprayD       sprayE       sprayF  
     3.7607       0.1160      -2.5158      -1.5963      -1.9512       0.2579  
summary(m2)

Call:
lm(formula = count_sqrt ~ spray, data = insetos2)

Residuals:
     Min       1Q   Median       3Q      Max 
-1.24486 -0.39970 -0.01902  0.42661  1.40089 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)   3.7607     0.1814  20.733  < 2e-16 ***
sprayB        0.1160     0.2565   0.452    0.653    
sprayC       -2.5158     0.2565  -9.807 1.64e-14 ***
sprayD       -1.5963     0.2565  -6.223 3.80e-08 ***
sprayE       -1.9512     0.2565  -7.606 1.34e-10 ***
sprayF        0.2579     0.2565   1.006    0.318    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 0.6283 on 66 degrees of freedom
Multiple R-squared:  0.7724,    Adjusted R-squared:  0.7552 
F-statistic:  44.8 on 5 and 66 DF,  p-value: < 2.2e-16
anova(m2)
Analysis of Variance Table

Response: count_sqrt
          Df Sum Sq Mean Sq F value    Pr(>F)    
spray      5 88.438 17.6876  44.799 < 2.2e-16 ***
Residuals 66 26.058  0.3948                      
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

7.3 Diagnóstico do modelo transformado

hist(m2$residuals,
     main = "Histograma dos resíduos do modelo m2",
     xlab = "Resíduos")

shapiro.test(m2$residuals)

    Shapiro-Wilk normality test

data:  m2$residuals
W = 0.98721, p-value = 0.6814
bartlett.test(count_sqrt ~ spray, data = insetos2)

    Bartlett test of homogeneity of variances

data:  count_sqrt by spray
Bartlett's K-squared = 3.7525, df = 5, p-value = 0.5856
check_normality(m2)
OK: residuals appear as normally distributed (p = 0.681).
check_heteroscedasticity(m2)
OK: Error variance appears to be homoscedastic (p = 0.854).
plot(simulateResiduals(m2))

8 Comparações múltiplas entre tratamentos

Se a ANOVA indicar diferença significativa entre grupos, o passo seguinte é investigar quais pares de tratamentos diferem entre si.

8.1 Médias ajustadas

O pacote emmeans calcula médias marginais estimadas, isto é, médias ajustadas com base no modelo.

library(emmeans)
Warning: package 'emmeans' was built under R version 4.5.3
Welcome to emmeans.
Caution: You lose important information if you filter this package's results.
See '? untidy'
medias_m1 <- emmeans(m1, ~ spray)
medias_m1
 spray emmean   SE df lower.CL upper.CL
 A      14.50 1.13 66   12.240    16.76
 B      15.33 1.13 66   13.073    17.59
 C       2.08 1.13 66   -0.177     4.34
 D       4.92 1.13 66    2.656     7.18
 E       3.50 1.13 66    1.240     5.76
 F      16.67 1.13 66   14.406    18.93

Confidence level used: 0.95 

8.2 Teste de Tukey

O procedimento de Tukey controla o erro do tipo I no conjunto das comparações par a par.

pairs(medias_m1, adjust = "tukey")
 contrast estimate  SE df t.ratio p.value
 A - B      -0.833 1.6 66  -0.520  0.9952
 A - C      12.417 1.6 66   7.755 <0.0001
 A - D       9.583 1.6 66   5.985 <0.0001
 A - E      11.000 1.6 66   6.870 <0.0001
 A - F      -2.167 1.6 66  -1.353  0.7542
 B - C      13.250 1.6 66   8.276 <0.0001
 B - D      10.417 1.6 66   6.506 <0.0001
 B - E      11.833 1.6 66   7.391 <0.0001
 B - F      -1.333 1.6 66  -0.833  0.9603
 C - D      -2.833 1.6 66  -1.770  0.4921
 C - E      -1.417 1.6 66  -0.885  0.9489
 C - F     -14.583 1.6 66  -9.108 <0.0001
 D - E       1.417 1.6 66   0.885  0.9489
 D - F     -11.750 1.6 66  -7.339 <0.0001
 E - F     -13.167 1.6 66  -8.223 <0.0001

P value adjustment: tukey method for comparing a family of 6 estimates 

8.3 Agrupamento de letras

Tratamentos que compartilham a mesma letra não diferem entre si ao nível de significância adotado.

library(multcomp)
Warning: package 'multcomp' was built under R version 4.5.3
Loading required package: mvtnorm
Warning: package 'mvtnorm' was built under R version 4.5.3
Loading required package: survival
Loading required package: TH.data
Warning: package 'TH.data' was built under R version 4.5.3
Loading required package: MASS

Attaching package: 'MASS'
The following object is masked from 'package:dplyr':

    select

Attaching package: 'TH.data'
The following object is masked from 'package:MASS':

    geyser
cld(medias_m1, Letters = LETTERS)
 spray emmean   SE df lower.CL upper.CL .group
 C       2.08 1.13 66   -0.177     4.34  A    
 E       3.50 1.13 66    1.240     5.76  A    
 D       4.92 1.13 66    2.656     7.18  A    
 A      14.50 1.13 66   12.240    16.76   B   
 B      15.33 1.13 66   13.073    17.59   B   
 F      16.67 1.13 66   14.406    18.93   B   

Confidence level used: 0.95 
P value adjustment: tukey method for comparing a family of 6 estimates 
significance level used: alpha = 0.05 
NOTE: If two or more means share the same grouping symbol,
      then we cannot show them to be different.
      But we also did not show them to be the same. 

9 GLM para dados de contagem

9.1 Por que considerar um GLM?

A resposta count é uma contagem. Em muitos casos, contagens são mais naturalmente modeladas por distribuições discretas, como a Poisson, em vez de uma distribuição normal.

No modelo de Poisson, assume-se:

\[ Y_i \sim \text{Poisson}(\mu_i) \]

com:

\[ P(Y_i = y_i) = \frac{e^{-\mu_i}\mu_i^{y_i}}{y_i!} \]

Além disso, usa-se uma função de ligação logarítmica:

\[ \log(\mu_i) = \eta_i = \beta_0 + \beta_1 x_{i1} + \cdots + \beta_p x_{ip} \]

No nosso caso, os preditores representam os tratamentos.

9.2 Ajuste do modelo de Poisson

m3 <- glm(count ~ spray,
          family = poisson,
          data = insetos)

summary(m3)

Call:
glm(formula = count ~ spray, family = poisson, data = insetos)

Coefficients:
            Estimate Std. Error z value Pr(>|z|)    
(Intercept)  2.67415    0.07581  35.274  < 2e-16 ***
sprayB       0.05588    0.10574   0.528    0.597    
sprayC      -1.94018    0.21389  -9.071  < 2e-16 ***
sprayD      -1.08152    0.15065  -7.179 7.03e-13 ***
sprayE      -1.42139    0.17192  -8.268  < 2e-16 ***
sprayF       0.13926    0.10367   1.343    0.179    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

(Dispersion parameter for poisson family taken to be 1)

    Null deviance: 409.041  on 71  degrees of freedom
Residual deviance:  98.329  on 66  degrees of freedom
AIC: 376.59

Number of Fisher Scoring iterations: 5

9.3 Comparações múltiplas no GLM

No contexto do modelo de Poisson, as médias estimadas na escala da resposta representam contagens esperadas.

df3 <- cld(
  emmeans(m3, ~ spray, type = "response"),
  Letters = LETTERS
)

df3
 spray  rate    SE  df asymp.LCL asymp.UCL .group
 C      2.08 0.417 Inf      1.41      3.08  A    
 E      3.50 0.540 Inf      2.59      4.74  AB   
 D      4.92 0.640 Inf      3.81      6.35   B   
 A     14.50 1.100 Inf     12.50     16.82    C  
 B     15.33 1.130 Inf     13.27     17.72    C  
 F     16.67 1.180 Inf     14.51     19.14    C  

Confidence level used: 0.95 
Intervals are back-transformed from the log scale 
P value adjustment: tukey method for comparing a family of 6 estimates 
Tests are performed on the log scale 
significance level used: alpha = 0.05 
NOTE: If two or more means share the same grouping symbol,
      then we cannot show them to be different.
      But we also did not show them to be the same. 

9.4 Gráfico das médias estimadas do GLM

df3 |>
  ggplot(aes(x = reorder(spray, -rate), y = rate, label = .group)) +
  geom_point() +
  geom_errorbar(aes(ymin = asymp.LCL, ymax = asymp.UCL), width = 0.1) +
  geom_text(aes(y = asymp.UCL), vjust = -0.5, size = 4) +
  theme_classic() +
  labs(
    x = "Inseticida",
    y = "Insetos vivos (contagem esperada)"
  ) +
  coord_flip()

10 Interpretação geral do fluxo analítico

Este exemplo mostra um fluxo bastante útil em análise de experimentos:

  1. começar pela exploração gráfica dos dados;
  2. ajustar um modelo simples e interpretável, como a ANOVA;
  3. verificar cuidadosamente os pressupostos;
  4. considerar alternativas, como transformação ou teste não paramétrico;
  5. usar um GLM quando a natureza da variável resposta justificar uma modelagem mais apropriada.

Em termos didáticos, a principal mensagem é que a escolha do método não deve ser automática. Ela depende do tipo de variável resposta, do comportamento dos resíduos e do objetivo inferencial.

11 Conclusões

  • A ANOVA de um fator é uma ferramenta central para comparar médias entre grupos.
  • Sua interpretação depende de pressupostos sobre os erros do modelo.
  • O teste de Kruskal-Wallis oferece uma alternativa quando esses pressupostos falham.
  • Transformações podem melhorar o comportamento dos dados, mas não substituem o raciocínio estatístico.
  • Para contagens, o GLM com distribuição de Poisson costuma ser uma alternativa conceitualmente mais coerente.

12 Sessão de pacotes

sessionInfo()
R version 4.5.2 (2025-10-31 ucrt)
Platform: x86_64-w64-mingw32/x64
Running under: Windows 11 x64 (build 26200)

Matrix products: default
  LAPACK version 3.12.1

locale:
[1] LC_COLLATE=English_United States.utf8 
[2] LC_CTYPE=English_United States.utf8   
[3] LC_MONETARY=English_United States.utf8
[4] LC_NUMERIC=C                          
[5] LC_TIME=English_United States.utf8    

time zone: America/Sao_Paulo
tzcode source: internal

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] multcomp_1.4-30    TH.data_1.1-5      MASS_7.3-65        survival_3.8-3    
 [5] mvtnorm_1.3-6      emmeans_2.0.2      agricolae_1.3-7    DHARMa_0.4.7      
 [9] performance_0.16.0 lubridate_1.9.5    forcats_1.0.1      stringr_1.6.0     
[13] dplyr_1.2.0        purrr_1.2.1        readr_2.2.0        tidyr_1.3.2       
[17] tibble_3.3.1       ggplot2_4.0.2      tidyverse_2.0.0   

loaded via a namespace (and not attached):
 [1] gtable_0.3.6        xfun_0.56           htmlwidgets_1.6.4  
 [4] insight_1.4.6       lattice_0.22-7      tzdb_0.5.0         
 [7] vctrs_0.7.1         tools_4.5.2         Rdpack_2.6.6       
[10] generics_0.1.4      sandwich_3.1-1      cluster_2.1.8.1    
[13] AlgDesign_1.2.1.2   pkgconfig_2.0.3     Matrix_1.7-4       
[16] RColorBrewer_1.1-3  S7_0.2.1            lifecycle_1.0.5    
[19] compiler_4.5.2      farver_2.1.2        codetools_0.2-20   
[22] gap.datasets_0.0.6  htmltools_0.5.9     yaml_2.3.12        
[25] pillar_1.11.1       nloptr_2.2.1        reformulas_0.4.4   
[28] boot_1.3-32         nlme_3.1-168        tidyselect_1.2.1   
[31] digest_0.6.39       stringi_1.8.7       labeling_0.4.3     
[34] splines_4.5.2       fastmap_1.2.0       grid_4.5.2         
[37] cli_3.6.5           magrittr_2.0.4      utf8_1.2.6         
[40] withr_3.0.2         scales_1.4.0        estimability_1.5.1 
[43] timechange_0.4.0    rmarkdown_2.30      otel_0.2.0         
[46] lme4_2.0-1          zoo_1.8-15          hms_1.1.4          
[49] evaluate_1.0.5      knitr_1.51          rbibutils_2.4.1    
[52] gap_1.14            rlang_1.1.7         Rcpp_1.1.1         
[55] xtable_1.8-8        glue_1.8.0          rstudioapi_0.18.0  
[58] minqa_1.2.8         jsonlite_2.0.0      R6_2.6.1           
[61] multcompView_0.1-11