1  Base Fosfato

A seguir, abordaremos a execução de uma análise exploratória e modelagem para o conjunto de dados fosfato.dta disponível em Everitt and Hothorn (2003). Estes dados correspondem às medições de fosfato inorgânico no plasma, obtidas a partir de 20 pacientes obesos e 13 controles, em diversos momentos após a administração de glicose oral (com \(t = 0, 30, 60, 90, 120, 150\) minutos).

Respota longitudinal: fosfato inorgâncio.

1.1 Análise exploratória

Para iniciarmos, vamos importar a base de dados (que se encontra no formato wide e com extensão .dta). Para tal, crie um projeto no RStudio e depois inclua os dados e o script no projeto. Em seguida, podemos executar os comandos a seguir.

#Ativando pacotes
library(haven)
library(dplyr)

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

#Transformando as variáveis qualitativas em fatores
base_fosfato = base_fosfato |> 
  mutate(grupo = factor(x = grupo, #nome da variável
                        levels = c(1,2), #valores observados para a variável
                        labels = c("Controle","Obeso")), #rótulos para os valores observados
         id = factor(id))

#Visualizando o objeto
base_fosfato
# A tibble: 33 × 8
   grupo    id       T0    T1    T2    T3    T4    T5
   <fct>    <fct> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
 1 Controle 1      4.30  3.30  3     2.60  2.20  2.5 
 2 Controle 2      3.70  2.60  2.60  1.90  2.90  3.20
 3 Controle 3      4     4.10  3.10  2.30  2.90  3.10
 4 Controle 4      3.60  3     2.20  2.80  2.90  3.90
 5 Controle 5      4.10  3.80  2.10  3     3.60  3.40
 6 Controle 6      3.80  2.20  2     2.60  3.80  3.60
 7 Controle 7      3.80  3     2.40  2.5   3.10  3.40
 8 Controle 8      4.40  3.90  2.80  2.10  3.60  3.80
 9 Controle 9      5     4     3.40  3.40  3.30  3.60
10 Controle 10     3.70  3.10  2.90  2.20  1.5   2.30
# ℹ 23 more rows

Vamos começar a análise, avaliando a existência de correlação entre a variável resposta medida nos diferentes momentos ao longo do tempo, tanto por meio de gráficos como por meio de medidas numéricas. Para isso, iremos selecionar somente as colunas referentes a variável resposta.

#Ativando pacote
library(GGally)

#Criando uma base somente com as variáveis T0, T1, T2, T3, T4, T5
base_var_resposta = base_fosfato |> 
  dplyr::select(T0, T1, T2, T3, T4, T5) #indicar as variáveis respostas

#Criando uma matriz gráfica com as correlações
ggpairs(data = base_var_resposta)

#Calculando a matriz de correlação
M = cor(base_var_resposta)
M
          T0        T1        T2        T3        T4        T5
T0 1.0000000 0.8549377 0.7462075 0.7396855 0.5303999 0.5660797
T1 0.8549377 1.0000000 0.7668919 0.7035983 0.4568444 0.4440564
T2 0.7462075 0.7668919 1.0000000 0.8441629 0.5079147 0.3825025
T3 0.7396855 0.7035983 0.8441629 1.0000000 0.6454788 0.5861326
T4 0.5303999 0.4568444 0.5079147 0.6454788 1.0000000 0.7289100
T5 0.5660797 0.4440564 0.3825025 0.5861326 0.7289100 1.0000000
#Ativando pacote
library("Hmisc")

#Testando a significância das correlações
teste_cor = rcorr(as.matrix(base_var_resposta))
teste_cor
     T0   T1   T2   T3   T4   T5
T0 1.00 0.85 0.75 0.74 0.53 0.57
T1 0.85 1.00 0.77 0.70 0.46 0.44
T2 0.75 0.77 1.00 0.84 0.51 0.38
T3 0.74 0.70 0.84 1.00 0.65 0.59
T4 0.53 0.46 0.51 0.65 1.00 0.73
T5 0.57 0.44 0.38 0.59 0.73 1.00

n= 33 


P
   T0     T1     T2     T3     T4     T5    
T0        0.0000 0.0000 0.0000 0.0015 0.0006
T1 0.0000        0.0000 0.0000 0.0075 0.0096
T2 0.0000 0.0000        0.0000 0.0025 0.0280
T3 0.0000 0.0000 0.0000        0.0000 0.0003
T4 0.0015 0.0075 0.0025 0.0000        0.0000
T5 0.0006 0.0096 0.0280 0.0003 0.0000       

Para conduzir análises específicas, será necessário, muitas vezes, modificar o formato da base de dados. As vezes precisaremos dela no formato wide, como foi na análise acima, outras vezes, no formato long. Por isso, é essencial dominar a transição entre os formatos wide e long para executar tais análises com sucesso.

#Ativando pacote
library(tidyr)

#Transformando uma base wide para long long
base_long = base_fosfato |> 
  gather(key = "Tempo", #nome da variável que irá armazenar o tempo
         value = "Fosfato", #nome da variável que irá armazenar a variável resposta
         3:8)

#Visualizando o objeto
base_long
# A tibble: 198 × 4
   grupo    id    Tempo Fosfato
   <fct>    <fct> <chr>   <dbl>
 1 Controle 1     T0       4.30
 2 Controle 2     T0       3.70
 3 Controle 3     T0       4   
 4 Controle 4     T0       3.60
 5 Controle 5     T0       4.10
 6 Controle 6     T0       3.80
 7 Controle 7     T0       3.80
 8 Controle 8     T0       4.40
 9 Controle 9     T0       5   
10 Controle 10    T0       3.70
# ℹ 188 more rows
#Passar do formato long para o wide
base_wide = base_long |> 
  spread(key = Tempo, #variável que dará nomes as colunas
         value = Fosfato) #variável que irá alimentar as colunas criadas

#Visualizando o objeto
base_wide
# A tibble: 33 × 8
   grupo    id       T0    T1    T2    T3    T4    T5
   <fct>    <fct> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
 1 Controle 1      4.30  3.30  3     2.60  2.20  2.5 
 2 Controle 2      3.70  2.60  2.60  1.90  2.90  3.20
 3 Controle 3      4     4.10  3.10  2.30  2.90  3.10
 4 Controle 4      3.60  3     2.20  2.80  2.90  3.90
 5 Controle 5      4.10  3.80  2.10  3     3.60  3.40
 6 Controle 6      3.80  2.20  2     2.60  3.80  3.60
 7 Controle 7      3.80  3     2.40  2.5   3.10  3.40
 8 Controle 8      4.40  3.90  2.80  2.10  3.60  3.80
 9 Controle 9      5     4     3.40  3.40  3.30  3.60
10 Controle 10     3.70  3.10  2.90  2.20  1.5   2.30
# ℹ 23 more rows

Usando a base no formato long, vamos criar boxplots para comparar o comportamento do fosfato em cada período de tempo avaliado.

#Relação dos padrões longitudinais
graf_boxplot = ggplot(data = base_long,
                      mapping = aes(x = Tempo, #variável que indicado o tempo
                                    y = Fosfato, #variável resposta
                                    fill = Tempo)) + #variável que indicado o tempo
  geom_boxplot() +
  guides(fill = "none") +
  theme_classic()

graf_boxplot

Podemos analisar o comportamento dos boxplots individualmente para cada grupo (controle e obesos).

#Relação dos padrões longitudinais com o grupo
graf_boxplot +
  facet_wrap(~grupo) #variável que será usada para "fatiar" o gráfico

Frequentemente, buscamos descrever os padrões longitudinais de maneira global e para grupos específicos de interesse.

#Descrevendo os padrões longitudinais globais
resumoGeral = base_long |>
  group_by(Tempo) |> #indicar a variável referente ao tempo
  summarise(n_obs = n(),
            media = mean(Fosfato, na.rm = TRUE), #indicar variável resposta
            d_p = sd(Fosfato, na.rm = TRUE), #indicar variável resposta
            minimo = min(Fosfato, na.rm = TRUE), #indicar variável resposta
            maximo = max(Fosfato, na.rm = TRUE), #indicar variável resposta
            erro_padrao = d_p/sqrt(n_obs),
            IC_LI = media - 1.96*erro_padrao,
            IC_LS = media + 1.96*erro_padrao) |>  
  ungroup()

#Visualziando o objeto
resumoGeral
# A tibble: 6 × 9
  Tempo n_obs media   d_p minimo maximo erro_padrao IC_LI IC_LS
  <chr> <int> <dbl> <dbl>  <dbl>  <dbl>       <dbl> <dbl> <dbl>
1 T0       33  4.36 0.709   3      6.60       0.123  4.12  4.60
2 T1       33  3.79 0.859   2.20   6.10       0.150  3.50  4.09
3 T2       33  3.36 0.807   2      5.20       0.140  3.09  3.64
4 T3       33  3.15 0.731   1.90   4.60       0.127  2.90  3.40
5 T4       33  3.14 0.713   1.5    4.70       0.124  2.89  3.38
6 T5       33  3.36 0.689   1.90   4.30       0.120  3.13  3.60
#Descrevendo os padrões longitudinais por grupos
resumoGrupo = base_long |>
  group_by(grupo,Tempo) |> #indicar a variável referente ao grupo e ao tempo
  summarise(n_obs = n(),
            media = mean(Fosfato, na.rm = TRUE), #indicar variável resposta
            d_p = sd(Fosfato, na.rm = TRUE), #indicar variável resposta
            minimo = min(Fosfato, na.rm = TRUE), #indicar variável resposta
            maximo = max(Fosfato, na.rm = TRUE), #indicar variável resposta
            erro_padrao = d_p/sqrt(n_obs),
            IC_LI = media - 1.96*erro_padrao,
            IC_LS = media + 1.96*erro_padrao) |> 
  ungroup()

#Visualziando o objeto
resumoGrupo
# A tibble: 12 × 10
   grupo    Tempo n_obs media   d_p minimo maximo erro_padrao IC_LI IC_LS
   <fct>    <chr> <int> <dbl> <dbl>  <dbl>  <dbl>       <dbl> <dbl> <dbl>
 1 Controle T0       20  4.15 0.516   3.10   5          0.115  3.92  4.38
 2 Controle T1       20  3.50 0.747   2.20   5          0.167  3.18  3.83
 3 Controle T2       20  2.95 0.541   2      4.10       0.121  2.71  3.19
 4 Controle T3       20  2.78 0.529   1.90   3.90       0.118  2.55  3.02
 5 Controle T4       20  3.00 0.622   1.5    3.80       0.139  2.73  3.27
 6 Controle T5       20  3.30 0.716   1.90   4.30       0.160  2.99  3.61
 7 Obeso    T0       13  4.68 0.858   3      6.60       0.238  4.21  5.14
 8 Obeso    T1       13  4.24 0.855   2.5    6.10       0.237  3.77  4.70
 9 Obeso    T2       13  4.00 0.740   2.30   5.20       0.205  3.60  4.40
10 Obeso    T3       13  3.70 0.658   2.20   4.60       0.183  3.34  4.06
11 Obeso    T4       13  3.35 0.815   2.10   4.70       0.226  2.90  3.79
12 Obeso    T5       13  3.46 0.661   2.30   4.20       0.183  3.10  3.82

Para criarmos gráficos que descrevem as trajetórias de cada paciente, vamos incialmente incluir uma variável tempo (no formato numérico) na base no formato long.

#Criando uma variável numérica para representar o tempo
base_long = base_long |> 
  mutate(Tempo_num = c(rep(0,33),rep(30,33),rep(60,33),rep(90,33),rep(120,33),rep(150,33)))

#Spaghetti plot individual
spaghetti = ggplot(data = base_long,
                   mapping = aes(x = Tempo_num, #variável que indica o tempo
                                 y = Fosfato, #indicar variável resposta
                                 group = id)) + #indicar variável que identifica o paciente
  geom_line() +
  theme_classic() +
  labs(x = "Tempo")

spaghetti

Vamos agora identificar a qual grupo cada trajetória pertence.

#Spaghetti plot por grupo
spaghetti_grupo = ggplot(data = base_long,
                   mapping = aes(x = Tempo_num, #variável que indica o tempo
                                 y = Fosfato, #indicar variável resposta
                                 group = id,  #indicar variável que identifica o paciente
                                 color = grupo)) +  #indicar variável grupo
  geom_line() +
  scale_x_continuous(breaks = seq(0,180,30)) +
  theme_classic() +
  labs(color = "Grupo",
       x = "Tempo")

spaghetti_grupo

#outra forma de fazer o spaghetti por grupo
spaghetti +
  facet_wrap(~grupo)

Vamos criar uma trajetória que represente o comportamento médio de todos os pacientes.

#Criando uma variável tempo (no formato numérico) no objeto resumoGeral
resumoGeral = resumoGeral |> 
  mutate(tempo_num = c(0,30,60,90,120,150))

#Gráfico com os perfis de médias por tempo
perfil_media = ggplot(data = resumoGeral, 
                      mapping = aes(x = tempo_num, 
                                    y = media)) +
  geom_line() +
  geom_point(size = 2.5) + 
  labs(x = "Tempo", 
       y = "Média do Fosfato") +
  geom_errorbar(mapping = aes(ymin = IC_LI, 
                              ymax = IC_LS), 
                width = 0.5) +
  scale_x_continuous(breaks = seq(0,150,30)) +
  theme_classic()

perfil_media

Também podemos apresentar as trajetórias médias para cada grupo.

#Criando uma variável tempo (no formato numérico) no objeto resumoGrupo
resumoGrupo = resumoGrupo |> 
  mutate(tempo_num = c(c(0,30,60,90,120,180),c(0,30,60,90,120,150)))

#Gráfico com os perfis de médias por tempo e por grupo
perfil_media_grupo = ggplot(data = resumoGrupo, 
                      mapping = aes(x = tempo_num, 
                                    y = media,
                                    color = grupo,
                                    shape = grupo)) +
  geom_line() +
  geom_point(size = 2.5) + 
  labs(x = "Tempo", 
       y = "Média do Fosfato",
       color = "Grupo") +
  geom_errorbar(mapping = aes(ymin = IC_LI, 
                              ymax = IC_LS), 
                width = 0.5) +
  scale_x_continuous(breaks = seq(0,150,30)) +
  guides(shape = "none") +
  theme_classic()

perfil_media_grupo

É possível ainda incluir as trajetórias individuais e as médias em um mesmo gráfico.

#Gráfico com os perfis de médias por tempo e por grupo com o spaghetti
perfil_media_grupo_spag = ggplot(data = resumoGrupo, 
                                 mapping = aes(x = tempo_num,
                                               y = media,
                                               color = grupo,
                                               shape = grupo)) +
  geom_line(data = base_long |> filter(grupo == "Controle"),
            mapping = aes(x = Tempo_num, 
                          y = Fosfato, 
                          group = id, 
                          color=" Pacientes Controle")) +
  geom_line(data = base_long |> filter(grupo == "Obeso"),
            mapping = aes(x = Tempo_num, 
                          y = Fosfato, 
                          group = id, 
                          color=" Pacientes Obesos")) +
  geom_line() +
  geom_point(size = 2.5) + 
  labs(x = "Tempo", 
       y = "Média do Fosfato",
       color = "Grupo") +
  geom_errorbar(mapping = aes(ymin = IC_LI, 
                              ymax = IC_LS), 
                width = 0.5) +
  scale_x_continuous(breaks = seq(0,180,30)) +
  scale_color_manual(values = c("grey90", "grey70", "#55acee", "#bb4444")) +
  guides(shape=FALSE) +
  theme_classic()

perfil_media_grupo_spag

1.2 Modelagem

1.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.

#Vamos inicialmente criar a variável tempo - média do tempo e esta medida ao quadrado
base_long = base_long |> 
  mutate(minC = Tempo_num - mean(Tempo_num, na.rm = TRUE),
         min2C = minC*minC)


#Ativando pacote
library(geepack)

#Ajustando o modelo com estrutura de correlação exchangeable
geeEXC = geeglm(formula = Fosfato ~ minC + min2C + grupo, #indicar a variável resposta ~ indicar as variáveis explicativas
                id = id, #variável que identifica os pacientes
                data = base_long, 
                family = gaussian(link = "identity"), #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 = Fosfato ~ minC + min2C + grupo, family = gaussian(link = "identity"), 
    data = base_long, id = id, corstr = "exchangeable")

 Coefficients:
              Estimate    Std.err    Wald Pr(>|W|)    
(Intercept)  2.988e+00  7.623e-02 1536.47  < 2e-16 ***
minC        -6.820e-03  9.614e-04   50.31 1.31e-12 ***
min2C        1.119e-04  2.081e-05   28.90 7.61e-08 ***
grupoObeso   6.222e-01  1.031e-01   36.44 1.57e-09 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Correlation structure = exchangeable 
Estimated Scale Parameters:

            Estimate Std.err
(Intercept)    0.459 0.04673
  Link = identity 

Estimated Correlation Parameters:
      Estimate Std.err
alpha        0       0
Number of clusters:   198  Maximum cluster size: 1 
#Ajustando o modelo com estrutura de correlação ar1
geeAR1 = geeglm(formula = Fosfato ~ minC + min2C + grupo, #indicar a variável resposta ~ indicar as variáveis explicativas
                id = id, #variável que identifica os pacientes
                data = base_long, #definição da variável resposta e função de ligação
                family = gaussian(link = "identity"), #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 = Fosfato ~ minC + min2C + grupo, family = gaussian(link = "identity"), 
    data = base_long, id = id, corstr = "ar1")

 Coefficients:
             Estimate   Std.err   Wald Pr(>|W|)    
(Intercept)  2.99e+00  7.62e-02 1536.5  < 2e-16 ***
minC        -6.82e-03  9.61e-04   50.3  1.3e-12 ***
min2C        1.12e-04  2.08e-05   28.9  7.6e-08 ***
grupoObeso   6.22e-01  1.03e-01   36.4  1.6e-09 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Correlation structure = ar1 
Estimated Scale Parameters:

            Estimate Std.err
(Intercept)    0.459  0.0467
  Link = identity 

Estimated Correlation Parameters:
      Estimate Std.err
alpha        0       0
Number of clusters:   198  Maximum cluster size: 1 

Comparando os modelos por meio do QIC.

#Comparando os modelos
QIC(geeEXC,geeAR1)
       QIC QICu Quasi Lik  CIC params QICC
geeEXC  99 98.9     -45.4 4.08      4  101
geeAR1  99 98.9     -45.4 4.08      4  101

1.2.2 Modelo de Efeitos Mistos

#Ativando pacote
library(lme4)

MMG = lmer(formula = Fosfato ~ minC + min2C + grupo + (1 | id), #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
           data = base_long)

summary(MMG)
Linear mixed model fit by REML ['lmerMod']
Formula: Fosfato ~ minC + min2C + grupo + (1 | id)
   Data: base_long

REML criterion at convergence: 355

Scaled residuals: 
    Min      1Q  Median      3Q     Max 
-2.3611 -0.5946  0.0425  0.5384  2.8970 

Random effects:
 Groups   Name        Variance Std.Dev.
 id       (Intercept) 0.274    0.523   
 Residual             0.206    0.454   
Number of obs: 198, groups:  id, 33

Fixed effects:
             Estimate Std. Error t value
(Intercept)  2.99e+00   1.30e-01   23.03
minC        -6.82e-03   6.29e-04  -10.84
min2C        1.12e-04   1.44e-05    7.79
grupoObeso   6.22e-01   1.98e-01    3.15

Correlation of Fixed Effects:
           (Intr) minC   min2C 
minC        0.000              
min2C      -0.291  0.000       
grupoObeso -0.601  0.000  0.000
fit warnings:
Some predictor variables are on very different scales: consider rescaling
#Ativando pacote
library(merTools)

#Plotando estimativa pontual e intervalar dos interceptos aleatórios
plotREsim(REsim(MMG))  # plot the interval estimates