2  Base Unhas

A seguir, abordaremos a execução de uma análise exploratória e modelagem para o conjunto de dados toenail.dta disponível em Rabe-Hesketh and Skronda (2008). Estes dados são referentes a um estudo clínico randomizado duplo-cego para tratamento de infecção de unha (dermatophyte onychomycosis), causada por um fungo. Foram coletadas medidas do grau de deslocamento da unha em \(t = 0, 4, 8, 12, 24, 36, 48\) semanas.

Respota longitudinal: Grau de deslocamento da unha.

2.1 Análise exploratória

Para prosseguir, vamos importar a base de dados (que se encontra no formato long e com extesnão .dta). Para tal, precisamos incluir a base de dados e o script no projeto criado, podemos executar os comandos a seguir.

#Ativando pacotes
library(haven)
library(dplyr)

#Importando o banco toenail.dta
base_unha = read_dta(file = "toenail.dta") #informar o nome do arquivo

#Transformando as variáveis treatment e patiente em fatores
base_unha = base_unha |> 
  mutate(treatment = factor(x = treatment, #nome da variável
                            levels = c(0,1), #valores observados para a variável
                            labels = c("Itraconazole","Terbinafine")), #rótulos para os valores observados
         patient = factor(patient))

#Visualizando o objeto
base_unha
# A tibble: 1,908 × 5
   patient outcome treatment     month visit
   <fct>     <dbl> <fct>         <dbl> <dbl>
 1 1             1 Terbinafine   0         1
 2 1             1 Terbinafine   0.857     2
 3 1             1 Terbinafine   3.54      3
 4 1             0 Terbinafine   4.54      4
 5 1             0 Terbinafine   7.54      5
 6 1             0 Terbinafine  10.0       6
 7 1             0 Terbinafine  13.1       7
 8 2             0 Itraconazole  0         1
 9 2             0 Itraconazole  0.964     2
10 2             1 Itraconazole  2         3
# ℹ 1,898 more rows

Vamos incializar a análise entendendo um pouco mais sobre o nosso banco de dados! Quantas medidas temos por paciente? Qual o número mínimo de medidas observadas? E qual o número máximo?

#Contabilizando o número de visitas por paciente
visitas_paciente = base_unha |> 
  group_by(patient) |> #indicar a variável que identifica o paciente no banco de dados
  summarise(num_visitas = n()) |> 
  ungroup() |>
  dplyr::select(num_visitas) 

#Ativando o pacote
library(janitor)

#Tabela de frequências para o número de visitas
visitas_paciente |> 
  tabyl(var1 = num_visitas) |> 
  adorn_totals(where = "row") |>
  adorn_pct_formatting()
 num_visitas   n percent
           1   5    1.7%
           2   3    1.0%
           3   7    2.4%
           4   6    2.0%
           5  10    3.4%
           6  39   13.3%
           7 224   76.2%
       Total 294  100.0%
#Calculando medidas descritivas para o núemro de visitas por paciente
visitas_paciente |> 
  summary()
  num_visitas  
 Min.   :1.00  
 1st Qu.:7.00  
 Median :7.00  
 Mean   :6.49  
 3rd Qu.:7.00  
 Max.   :7.00  

Agora, vamos contabilizar a proporção de casos moderados/severos em cada visita para cada grupo de tratamento e na sequência plotar essas proporções.

#Contabilizando as porporções de casos moderados/severos por visita para cada tratamento
prop_visita = base_unha |>
  group_by(treatment,visit) |> #indicar as variáveis associadas ao grupo e ao tempo (números inteiros indicando qual a visita)
  summarise(prop = mean(outcome, na.rm = TRUE)) |> #variável resposta (precisa estar ser binária (0 e 1))
  ungroup()

prop_visita
# A tibble: 14 × 3
   treatment    visit   prop
   <fct>        <dbl>  <dbl>
 1 Itraconazole     1 0.370 
 2 Itraconazole     2 0.348 
 3 Itraconazole     3 0.319 
 4 Itraconazole     4 0.220 
 5 Itraconazole     5 0.108 
 6 Itraconazole     6 0.0855
 7 Itraconazole     7 0.105 
 8 Terbinafine      1 0.372 
 9 Terbinafine      2 0.327 
10 Terbinafine      3 0.276 
11 Terbinafine      4 0.207 
12 Terbinafine      5 0.0602
13 Terbinafine      6 0.0630
14 Terbinafine      7 0.0458
#Ativando pacote
library(ggplot2)

#Plotando as proporções contabilizadas
prop_visita |> 
  ggplot(mapping = aes(x = visit, #indicar a variável de tempo
                       y = prop)) +
  geom_bar(stat = "identity") +
  labs(x = "Visita",
       y = "Proporção") +
  theme_classic() +
  facet_wrap(~treatment)

Agora, vamos fazer o lorelograma.

#Selecionando somente as variáveis que indicam o paciente, a visita e a variável resposta
base_lorelogram = base_unha |> 
  dplyr::select(patient,visit, outcome) #indique as variáveis que identificam: paciente, tempo e desfecho

#Ativando o pacote
library(tidyr)
library(lorelogram)

#Transformando em uma base de dados balanceada 
base_lorelogram_balan = base_lorelogram |>  
  complete(patient, visit) #indicar as variáveis que identificam: paciente e tempo

#Criando o lorelograma
lorelogram(data = as.data.frame(base_lorelogram_balan), 
           data_format = "long")

# A tibble: 5 × 4
    Lag   LORs U_95_CI L_95_CI
  <dbl>  <dbl>   <dbl>   <dbl>
1     1  3.41    3.95   2.88  
2     2  1.48    1.96   0.997 
3     3  0.614   1.21   0.0214
4     4  0.105   0.956 -0.745 
5     5 -0.775   0.403 -1.95  

2.2 Modelagem

2.2.1 GEE

Vamos iniciar construindo um modelo marginal: Equações de Estimação Generalizada (GEE). Para especificar o modelo é preciso defirnir:

  • a distribuição de probabilidade da variável resposta (família).

  • a função de ligação.

  • a estrutura de correlação.

#Ativando pacote
library(geepack)

#Ajustando o modelo com estrutura de correlação exchangeable
geeEXC = geeglm(formula = outcome ~ treatment + visit, #indicar a variável resposta ~ indicar as variáveis explicativas
                id = patient, #variável que identifica os pacientes
                data = base_unha, 
                family = binomial(link = "logit"), #definição da variável resposta e função de ligação
                corstr = "exchangeable") #definição da estrutura de correlação

summary(geeEXC)

Call:
geeglm(formula = outcome ~ treatment + visit, family = binomial(link = "logit"), 
    data = base_unha, id = patient, corstr = "exchangeable")

 Coefficients:
                     Estimate  Std.err    Wald Pr(>|W|)    
(Intercept)           0.05265  0.19566   0.072    0.788    
treatmentTerbinafine -0.01045  0.24767   0.002    0.966    
visit                -0.37967  0.03629 109.447   <2e-16 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Correlation structure = exchangeable 
Estimated Scale Parameters:

            Estimate Std.err
(Intercept)   0.9714  0.1504
  Link = identity 

Estimated Correlation Parameters:
      Estimate Std.err
alpha   0.4409 0.09229
Number of clusters:   294  Maximum cluster size: 7 
#Ativando pacote
library(broom.mixed)

#Calculando OR e seus intervalos de confiança
tidy(geeEXC,
     conf.int=TRUE,
     exponentiate=TRUE) |> 
  dplyr::select(estimate, conf.low, conf.high)
# A tibble: 3 × 3
  estimate conf.low conf.high
     <dbl>    <dbl>     <dbl>
1    1.05     0.718     1.55 
2    0.990    0.609     1.61 
3    0.684    0.637     0.735
#Ajustando o modelo com estrutura de correlação ar1
geeAR1 = geeglm(formula = outcome ~ treatment + visit, #indicar a variável resposta ~ indicar as variáveis explicativas
                id = patient, #variável que identifica os pacientes
                data = base_unha, 
                family = binomial(link = "logit"), #definição da variável resposta e função de ligação
                corstr = "ar1") #definição da estrutura de correlação

summary(geeAR1)

Call:
geeglm(formula = outcome ~ treatment + visit, family = binomial(link = "logit"), 
    data = base_unha, id = patient, corstr = "ar1")

 Coefficients:
                     Estimate Std.err  Wald Pr(>|W|)    
(Intercept)           -0.0937  0.1765  0.28     0.60    
treatmentTerbinafine  -0.1187  0.2150  0.31     0.58    
visit                 -0.3342  0.0346 93.43   <2e-16 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Correlation structure = ar1 
Estimated Scale Parameters:

            Estimate Std.err
(Intercept)    0.966   0.127
  Link = identity 

Estimated Correlation Parameters:
      Estimate Std.err
alpha    0.697  0.0642
Number of clusters:   294  Maximum cluster size: 7 
#Calculando OR e seus intervalos de confiança
tidy(geeAR1,
     conf.int=TRUE,
     exponentiate=TRUE) |> 
  dplyr::select(estimate, conf.low, conf.high)
# A tibble: 3 × 3
  estimate conf.low conf.high
     <dbl>    <dbl>     <dbl>
1    0.911    0.644     1.29 
2    0.888    0.583     1.35 
3    0.716    0.669     0.766

Comparando as diferentes estruturas de correlação por meio do QIC.

#Comparando os modelos
QIC(geeEXC,geeAR1)
        QIC QICu Quasi Lik  CIC params QICC
geeEXC 1838 1825      -909 9.54      3 1838
geeAR1 1835 1825      -910 8.01      3 1835

2.2.2 Modelo de Efeitos Mistos

Inicialemnte, vamos ajustar o modelo com interceptos aleatórios.

#Ativando pacote
library(lme4)

#Ajuste do Modelo Misto com interceptos aleatórios
MML= glmer(formula = outcome ~ treatment + visit + (1 | patient), #indicar a variável resposta ~ indicar as variáveis explicativas (1 | variável que identifica o paciente) é a forma de incluir o intercepto aleatório
           family = binomial(link = "logit"), #definição da variável resposta e função de ligação
           data = base_unha)

summary(MML)
Generalized linear mixed model fit by maximum likelihood (Laplace
  Approximation) [glmerMod]
 Family: binomial  ( logit )
Formula: outcome ~ treatment + visit + (1 | patient)
   Data: base_unha

     AIC      BIC   logLik deviance df.resid 
    1260     1283     -626     1252     1904 

Scaled residuals: 
   Min     1Q Median     3Q    Max 
-4.951 -0.163 -0.049 -0.011 17.151 

Random effects:
 Groups  Name        Variance Std.Dev.
 patient (Intercept) 22       4.69    
Number of obs: 1908, groups:  patient, 294

Fixed effects:
                     Estimate Std. Error z value Pr(>|z|)    
(Intercept)           -1.0518     0.7994   -1.32     0.19    
treatmentTerbinafine  -0.6969     0.6870   -1.01     0.31    
visit                 -0.9115     0.0743  -12.26   <2e-16 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Correlation of Fixed Effects:
            (Intr) trtmnT
trtmntTrbnf -0.345       
visit        0.221  0.092
#Calculando OR e seus intervalos de confiança
tidy(MML,
     conf.int=TRUE,
     exponentiate=TRUE,
     effects="fixed") |> 
  dplyr::select(estimate, conf.low, conf.high)
# A tibble: 3 × 3
  estimate conf.low conf.high
     <dbl>    <dbl>     <dbl>
1    0.349   0.0729     1.67 
2    0.498   0.130      1.91 
3    0.402   0.347      0.465
#Ativando pacote
library(performance)

#Obtendo o Coeficiente de correlação Intraclasse
icc(model = MML)
# Intraclass Correlation Coefficient

    Adjusted ICC: 0.870
  Unadjusted ICC: 0.765
#Ativando pacote
library(merTools)

#Plotando estimativa pontual e intervalar dos interceptos aleatórios
plotREsim(REsim(MML))  

Agora vamos ajustar, o modelo com interceptos e coeficientes aleatórios.

#Ajuste do Modelo Misto com interceptos e coeficientes aleatórios
MML2= glmer(formula = outcome ~ treatment + visit + (1 | patient) + (visit | patient),
           family = binomial(link = "logit"), 
           data = base_unha)

summary(MML2)
Generalized linear mixed model fit by maximum likelihood (Laplace
  Approximation) [glmerMod]
 Family: binomial  ( logit )
Formula: outcome ~ treatment + visit + (1 | patient) + (visit | patient)
   Data: base_unha

     AIC      BIC   logLik deviance df.resid 
     977     1016     -482      963     1901 

Scaled residuals: 
    Min      1Q  Median      3Q     Max 
-1.7780 -0.0042 -0.0026 -0.0001  2.5874 

Random effects:
 Groups    Name        Variance Std.Dev. Corr 
 patient   (Intercept) 4.76e-02  0.218        
 patient.1 (Intercept) 2.20e+03 46.902        
           visit       7.90e+01  8.887   -0.98
Number of obs: 1908, groups:  patient, 294

Fixed effects:
                     Estimate Std. Error z value Pr(>|z|)    
(Intercept)           -10.254      1.025  -10.00   <2e-16 ***
treatmentTerbinafine   -1.015      1.042   -0.97     0.33    
visit                  -0.217      0.379   -0.57     0.57    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Correlation of Fixed Effects:
            (Intr) trtmnT
trtmntTrbnf -0.281       
visit       -0.475  0.027
#Calculando OR e seus intervalos de confiança
tidy(MML2,
     conf.int=TRUE,
     exponentiate=TRUE,
     effects="fixed") |> 
  dplyr::select(estimate, conf.low, conf.high)
# A tibble: 3 × 3
   estimate   conf.low conf.high
      <dbl>      <dbl>     <dbl>
1 0.0000352 0.00000472  0.000262
2 0.362     0.0470      2.79    
3 0.805     0.383       1.69    
#Obtendo o Coeficiente de correlação Intraclasse
icc(model = MML2)
# Intraclass Correlation Coefficient

    Adjusted ICC: 0.014
  Unadjusted ICC: 0.013
#Ativando pacote
library(merTools)

#Plotando estimativa pontual e intervalar dos efeitos aleatórios
plotREsim(REsim(MML2))

#Plotando de forma separada
plotREsim(REsim(MML2),
          facet= list(groupFctr= "patient", term= "(Intercept)"))

plotREsim(REsim(MML2),
          facet= list(groupFctr= "patient", term= "visit"))