1. Paquetes necesarios.

library(tidyverse)
## Warning: package 'tidyverse' was built under R version 4.4.3
## Warning: package 'tidyr' was built under R version 4.4.3
## Warning: package 'dplyr' was built under R version 4.4.3
## Warning: package 'forcats' was built under R version 4.4.3
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.1.5
## ✔ forcats   1.0.0     ✔ stringr   1.5.1
## ✔ ggplot2   3.5.1     ✔ tibble    3.2.1
## ✔ lubridate 1.9.3     ✔ tidyr     1.3.1
## ✔ purrr     1.0.2     
## ── 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
library(knitr)
## Warning: package 'knitr' was built under R version 4.4.2
library(data.table)
## Warning: package 'data.table' was built under R version 4.4.3
## 
## Adjuntando el paquete: 'data.table'
## 
## The following objects are masked from 'package:lubridate':
## 
##     hour, isoweek, mday, minute, month, quarter, second, wday, week,
##     yday, year
## 
## The following objects are masked from 'package:dplyr':
## 
##     between, first, last
## 
## The following object is masked from 'package:purrr':
## 
##     transpose
library(haven)
library(geepack)
## Warning: package 'geepack' was built under R version 4.4.3
library(corrplot)
## Warning: package 'corrplot' was built under R version 4.4.2
## corrplot 0.95 loaded
library(psych)
## Warning: package 'psych' was built under R version 4.4.3
## 
## Adjuntando el paquete: 'psych'
## 
## The following objects are masked from 'package:ggplot2':
## 
##     %+%, alpha
library(kableExtra)
## Warning: package 'kableExtra' was built under R version 4.4.2
## 
## Adjuntando el paquete: 'kableExtra'
## 
## The following object is masked from 'package:dplyr':
## 
##     group_rows
options(scipen=999, digits=3)

2. Cargar el conjunto de datos read_dta.

CD4_Ejercicio1 <- read_dta("C:/Users/Usuario/Desktop/iecs/ano_2/análisis_datos_longitudinales/encuentro_sincrónico_29_08_2025/CD4 Ejercicio1.dta")

3. Explorar observaciones con datos faltantes.

colSums(is.na(CD4_Ejercicio1))
##        id      time       cd4       Age    gender       sex Treatment 
##         0         0        42         0         0         0         0

Base de datos CD4.

Diferencia entre formato LONG y WIDE en análisis longitudinal.

En estudios longitudinales, un mismo individuo tiene varias mediciones repetidas a lo largo del tiempo.

La forma en que organizamos los datos puede ser:

Formato LONG
- Cada fila corresponde a una observación en un tiempo específico.
- Ejemplo:

id time cd4
1 0 500
1 6 550
1 12 600
2 0 400
2 6 430

Formato WIDE
- Cada fila corresponde a un individuo y cada columna a un tiempo de medición.
- Ejemplo:

id cd4_0 cd4_6 cd4_12
1 500 550 600
2 400 430 NA

👉 En la práctica:
- Usamos formato LONG para análisis longitudinales (modelos mixtos, GEE, etc.).
- Usamos formato WIDE para correlaciones entre tiempos o resúmenes descriptivos.

4. Consigna.

1.- Evaluar el cambio de CD4 en el tiempo ajustando por edad y sexo como posibles confundidores.

2.- Evaluar el cambio de CD4 en función del tiempo en HOMBRES y MUJERES.

3.- Evaluar el cambio de CD4 en función del tiempo en los grupos de TRATAMIENTO.

Base WIDE.

CD4_Ejercicio1$Treatment <- as.numeric(CD4_Ejercicio1$Treatment)  

CD4_WIDE <- CD4_Ejercicio1 %>% 
  pivot_wider(names_from = time, ## selecciono la variable repetida
              names_prefix = "Tiempo", ## agrego el prefijo para el nuevo nombre
              values_from = cd4)  ### selecciono de donde va a tomar los valores

CD4_WIDE[1:10,-3] 
## # A tibble: 10 × 7
##    id             Age   sex Treatment Tiempo12 Tiempo6 Tiempo0
##    <dbl+lbl>    <dbl> <dbl>     <dbl>    <dbl>   <dbl>   <dbl>
##  1 18 [DIC-107]    23     1         0      529     281     364
##  2 47 [JB-11]      30     1         0      506     605     311
##  3 11 [BKC-32]     33     1         0      735     780     554
##  4 71 [MLL-61*]    47     1         0      670     638     468
##  5 69 [ML-37]      44     1         0      365     487     281
##  6 65 [MDL-81]     38     0         0     1024     850     704
##  7 14 [CCG-45]     35     1         0      613     524     377
##  8 75 [NEC-119]    31     1         0      729     590     521
##  9 20 [DK-04]      34     1         0      682     777     652
## 10 62 [LFD-39]     26     1         0      434     372     607

pivot_wider(...) Convierte la base de formato LONG a WIDE. Entonces, en vez de tener 3 filas por individuo (una para cada tiempo), pasa a una sola fila con 3 columnas distintas.

CD4_WIDE[1:10, -3] Muestra solo las primeras 10 filas de la nueva base CD4_WIDE y excluye la columna número 3.

👉 En resumen: este chunk sirve para reorganizar la base en formato WIDE y mostrar un vistazo de las primeras 10 filas sin una de las columnas. Es útil para análisis descriptivos o para ver las mediciones repetidas de CD4 en una sola línea por paciente.

Base LONG.

head(CD4_Ejercicio1[order(CD4_Ejercicio1$id, decreasing = F),]) # %>% kable() 
## # A tibble: 6 × 7
##   id          time   cd4   Age gender   sex Treatment
##   <dbl+lbl>  <dbl> <dbl> <dbl> <chr>  <dbl>     <dbl>
## 1 1 [ACE-12]     0   311    62 M          1         1
## 2 1 [ACE-12]     6   473    62 M          1         1
## 3 1 [ACE-12]    12   408    62 M          1         1
## 4 2 [AES-47]     0   436    25 M          1         0
## 5 2 [AES-47]     6   442    25 M          1         0
## 6 2 [AES-47]    12   643    25 M          1         0

¿Existe correlación entre las medidas repetidas?

corrplot.mixed(cor(CD4_WIDE[,6:8], # selecciono las variables dependientes correlacionadas
                   use = "pairwise.complete"), # correlacion
               lower = "number", # panel inferior = número
               upper = "circle", # panel superior = Círculos
         title = "Correlacion CD4") # título

Sí, hay correlación 👍.

En el gráfico:

Todas son positivas y moderadas a fuertes → significa que los valores de CD4 en un mismo paciente están relacionados a lo largo del tiempo (lo esperable en datos longitudinales, porque un paciente con CD4 alto al inicio tiende a tener CD4 alto después).

🔹 ¿Para qué sirve saberlo?

  1. Evitar un supuesto incorrecto: si tratás las observaciones como independientes (como si fueran pacientes distintos), subestimarías la variabilidad y podrías inflar el poder estadístico (o sea, creés que tenés más información de la que realmente hay).
  2. Elegir el modelo adecuado:
  1. Definir estructura de correlación: en GEE podés elegir independence, exchangeable, unstructured, etc. Saber cómo se relacionan las mediciones en el tiempo te ayuda a decidir cuál estructura se ajusta mejor.

👉 Resumen simple: sí hay correlación, y eso justifica que en análisis longitudinales no se usen modelos clásicos de regresión lineal sin más, sino modelos que respeten esa dependencia entre medidas repetidas.

5. Análisis longitudinal: evaluación efecto del género.

Utilizamos la base en formato LONG.

CD4_Long <- CD4_WIDE %>%  # selecciono base wide
  pivot_longer(
    cols = starts_with("Tiempo"), #  columnas que empiezan con tiempo (0, 6 ,12)
    names_to = "tiempo_nuevo", #  Nueba variable que va a contener tiempo(0, 6, 12)
    names_transform = as.integer, # transformo tiempo a integer-numerica
    names_prefix = "Tiempo",  # saco la palabra tiempo para que no quede tiempo6 como valor
    values_to = c("cd4_nuevo") # los valores de cd4 de cada tiempo a la nueva variable 
  )

head(CD4_Long)
## # A tibble: 6 × 7
##   id             Age gender   sex Treatment tiempo_nuevo cd4_nuevo
##   <dbl+lbl>    <dbl> <chr>  <dbl>     <dbl>        <int>     <dbl>
## 1 18 [DIC-107]    23 M          1         0           12       529
## 2 18 [DIC-107]    23 M          1         0            6       281
## 3 18 [DIC-107]    23 M          1         0            0       364
## 4 47 [JB-11]      30 M          1         0           12       506
## 5 47 [JB-11]      30 M          1         0            6       605
## 6 47 [JB-11]      30 M          1         0            0       311

¿Estará correcto en relación a la base original?

cbind(CD4_Ejercicio1%>% select(id, time,cd4) %>% filter(id %in% 18), 
CD4_Long%>% select(id, tiempo_nuevo, cd4_nuevo) %>% filter(id %in% 18))
##   id time cd4 id tiempo_nuevo cd4_nuevo
## 1 18   12 529 18           12       529
## 2 18    6 281 18            6       281
## 3 18    0 364 18            0       364

🔹 ¿Qué hace ese código?

🔹 ¿Es necesario hacerlo?

🔹 ¿No alcanza con LONG?

6. Modelo de regresión lineal asumiendo INDEPENDENCIA de las observaciones.

mod_lineal <- lm(cd4~time +Age + sex, data = CD4_Ejercicio1)  
summary(mod_lineal) 
## 
## Call:
## lm(formula = cd4 ~ time + Age + sex, data = CD4_Ejercicio1)
## 
## Residuals:
##    Min     1Q Median     3Q    Max 
## -531.2 -149.0  -18.7  123.7  748.3 
## 
## Coefficients:
##             Estimate Std. Error t value      Pr(>|t|)    
## (Intercept)  479.846     71.738    6.69 0.00000000013 ***
## time          17.224      2.703    6.37 0.00000000083 ***
## Age            0.857      1.529    0.56          0.58    
## sex          -13.929     55.412   -0.25          0.80    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 217 on 263 degrees of freedom
##   (42 observations deleted due to missingness)
## Multiple R-squared:  0.135,  Adjusted R-squared:  0.125 
## F-statistic: 13.7 on 3 and 263 DF,  p-value: 0.000000026

🔹 Supuesto

Se está usando un lm() (regresión lineal clásica), que asume independencia de las observaciones. Pero como tus datos son longitudinales (varias mediciones del mismo paciente), este supuesto no es realista. Igual, sirve como “primer aproximación” para comparar después con los modelos longitudinales que sí corrigen la correlación.

Resultados

1. Intercepto (β0 = 479.846, p < 0.001): → Es el valor promedio de CD4 cuando time = 0, Age = 0 y sex = 0. - En la práctica, este valor “base” no tiene interpretación clínica directa (edad 0 no existe), pero marca el punto de partida del modelo.

2. Efecto del tiempo (β1 = 17.224, p = 0.008): → Cada unidad de aumento en time (probablemente meses) se asocia a un aumento promedio de 17 células CD4, ajustando por edad y sexo. ✅ Es significativo, o sea que el tiempo sí está asociado a cambios en CD4.

3. Edad (β2 = 0.857, p = 0.58): → La edad no tiene efecto significativo sobre los valores de CD4 en este modelo. Cada año de más, en promedio, se asocia con apenas +0.86 células CD4 (no significativo).

4. Sexo (β3 = -13.929, p = 0.77): → El sexo tampoco muestra un efecto significativo. Según el codificado (1 = hombre, 0 = mujer), los hombres tendrían ~14 CD4 menos en promedio que las mujeres, pero sin significancia estadística.

Conclusión rápida:

7. Modelo de regresión lineal asumiendo DEPENDENCIA de las observaciones (correlación no estructurada).

mod_gee <- geeglm(cd4 ~ time + Age + sex ,   # y~ covariables
                     data = CD4_Ejercicio1,   # datos
                     id = CD4_Ejercicio1$id,   # id= identificador del grupo al que corresponden las                                                  medidas repetidas
                    family = gaussian,         # modelo lineal (existen otras: poisson,gaussian, etc)
                    corstr = "unstructured") # tipo de correlación ("exchangeable", "independence" ,                                                "ar1", etc)
options(scipen=999, digits=3)
tidy(mod_gee, conf.int = T)
## # A tibble: 4 × 7
##   term        estimate std.error statistic  p.value conf.low conf.high
##   <chr>          <dbl>     <dbl>     <dbl>    <dbl>    <dbl>     <dbl>
## 1 (Intercept)  481.        63.6    57.3    3.77e-14   357.      606.  
## 2 time          17.6        2.81   39.2    3.78e-10    12.1      23.1 
## 3 Age            0.734      1.43    0.265  6.07e- 1    -2.06      3.53
## 4 sex          -13.1       48.6     0.0722 7.88e- 1  -108.       82.3

📊 Resultados principales (imagen que mostraste):

Intercepto = 481.4 (IC95%: 356.7 a 606.1, p < 0.001) → Cuando time = 0, edad = 0 y sexo = 0 (es decir, referencia: mujeres de edad 0 en el basal), el valor esperado de CD4 es ~481. → Obviamente “edad 0” es solo la referencia del modelo, no un valor real.

time = 17.6 (IC95%: 12.1 a 23.1, p < 0.001) → Por cada unidad de tiempo (ejemplo: cada visita en meses), los CD4 aumentan en promedio +17.6 células, de manera significativa. → Es el resultado más relevante: muestra una tendencia clara al aumento de CD4 a lo largo del seguimiento.

Age = 0.73 (IC95%: -2.06 a 3.53, p = 0.61) → No hay efecto significativo de la edad en la evolución de CD4. → O sea, dentro de esta cohorte, los CD4 no dependen de la edad de los participantes.

Sex = -13.1 (IC95%: -108.4 a 82.3, p = 0.79) → No se encontró diferencia significativa entre hombres y mujeres. → El valor negativo indica que los hombres tenderían a tener ~13 CD4 menos que las mujeres, pero como el intervalo es muy amplio y pasa por 0, no es concluyente.

🔎 Conclusión rápida:

Tiempo → sí tiene un efecto significativo. Los CD4 aumentan conforme pasa el tiempo.

Edad y sexo → no influyen significativamente en el cambio de CD4 en este modelo.

Ventaja del GEE → A diferencia del modelo lineal simple, aquí se está corrigiendo el problema de correlación intra-sujeto (porque cada persona tiene varias mediciones). Esto hace que los errores estándar y los IC sean más realistas.

👉 En resumen: la tendencia temporal es robusta y significativa. El resto de covariables (edad y sexo) no aportan cambios significativos en CD4 en este análisis.

8. Gráfico de predicción lineal del efecto del tiempo sobre cambio de CD4 con IC95%.

ggplot(CD4_Ejercicio1, aes(time,cd4 )) + geom_point()+
 geom_smooth(method = "lm", se = TRUE) + 
  theme_classic()+ 
 labs(x = "Tiempo", y = "CD4", title = "Efectos marginales")
## `geom_smooth()` using formula = 'y ~ x'
## Warning: Removed 42 rows containing non-finite outside the scale range
## (`stat_smooth()`).
## Warning: Removed 42 rows containing missing values or values outside the scale range
## (`geom_point()`).

📈 Qué muestra

👉 En resumen:

9. Gráfico de predicción lineal del efecto del tiempo sobre cambio de CD4.

# Comparo predichos
datospredichosGEE <- data.frame(na.omit(CD4_Ejercicio1), ## sacamos datos faltantes 
                                                         # o no podemos graficar
                                phat = fitted(mod_gee))

ggplot(datospredichosGEE, aes(time, phat)) + 
 geom_smooth(method = "lm") +
ylim (20, 1400) +
  theme_classic()+ 
 labs(x = "Tiempo", y = "CD4", title = "Efectos marginales") 
## `geom_smooth()` using formula = 'y ~ x'

🔎 Interpretación:

📌 En resumen:

10. Modelo de regresión lineal asumiendo DEPENDENCIA y evaluando interacción entre sexo y tiempo.

mod_gee2 <- geeglm(cd4 ~ time*sex  ,
                     data = CD4_Ejercicio1,
                     id = CD4_Ejercicio1$id,
                     corstr = "unstructured")
tidy(mod_gee2, conf.int = T)
## # A tibble: 4 × 7
##   term        estimate std.error statistic  p.value conf.low conf.high
##   <chr>          <dbl>     <dbl>     <dbl>    <dbl>    <dbl>     <dbl>
## 1 (Intercept)   486.       75.8    41.2    1.40e-10   338.       635. 
## 2 time           20.7       9.95    4.32   3.78e- 2     1.17      40.2
## 3 sex            10.1      78.5     0.0166 8.98e- 1  -144.       164. 
## 4 time:sex       -3.24     10.4     0.0973 7.55e- 1   -23.6       17.1

Interpretación:

👉 El cambio de CD4 a lo largo del tiempo NO es significativamente diferente entre hombres y mujeres.

11. Análisis longitudinal: evaluación del efecto del tratamiento.

Analisis exploratorio: media de CD4 en cada medición por grupo de tratamiento

CD4_WIDE %>% group_by(Treatment) %>% summarise(T0 = mean(Tiempo0, na.rm = T), 
                                               T6 = mean(Tiempo6, na.rm = T), 
                                               T12 = mean(Tiempo12, na.rm = T)) 
## # A tibble: 2 × 4
##   Treatment    T0    T6   T12
##       <dbl> <dbl> <dbl> <dbl>
## 1         0  535.  601.  648.
## 2         1  468.  631.  730.

1. Exploratorio (tabla de medias por grupo y tiempo).

Grupo 0 (sin tratamiento):

T0 = 535

T6 = 601

T12 = 648

Grupo 1 (tratamiento):

T0 = 468

T6 = 631

T12 = 730

👉 Ambos grupos aumentan CD4 con el tiempo, pero el grupo tratado arranca con menos CD4 (468 vs 535) y luego supera al no tratado (730 vs 648 a los 12 meses).

2. Respuestas a las preguntas:

  1. ¿Existen diferencias basales entre grupos? Sí. El grupo tratado tiene menor CD4 basal (~468 vs 535), diferencia observable ya desde el inicio.

  2. ¿Existe efecto del tiempo? Sí. En ambos grupos los CD4 aumentan a lo largo del seguimiento (de T0 a T6 y T12).

  3. ¿Existe efecto del tratamiento? Sí. Aunque empieza con menor CD4, el grupo tratado muestra un incremento mayor:

No tratados: +113 entre T0 y T12.

Tratados: +262 entre T0 y T12. Esto sugiere un beneficio adicional del tratamiento en la recuperación de CD4.

Conclusión rápida:

12. Modelo marginal (promedio poblacional) de regresion lineal asumiendo dependencia.

Ver efecto del tiempo (magnitud del cambio por mes) y el del tratamiento (¿existe diferencia basal de CD4 entre los grupos?)

mod_gee3 <- geeglm(cd4 ~ time +Treatment + sex ,
                     data = CD4_Ejercicio1,
                     id = CD4_Ejercicio1$id,
                     corstr = "unstructured")
tidy(mod_gee3, conf.int = T)
## # A tibble: 4 × 7
##   term        estimate std.error statistic  p.value conf.low conf.high
##   <chr>          <dbl>     <dbl>     <dbl>    <dbl>    <dbl>     <dbl>
## 1 (Intercept)   495.       54.1    83.7    0           389.      601. 
## 2 time           17.7       2.84   39.0    4.15e-10     12.2      23.3
## 3 Treatment      11.9      26.2     0.206  6.50e- 1    -39.5      63.3
## 4 sex            -7.77     48.7     0.0254 8.73e- 1   -103.       87.7

Este es el modelo marginal GEE (promedio poblacional) asumiendo dependencia. Vamos a interpretar lo que aparece en el output (mod_gee3):

Resultados principales:

Intercepto (495, p<0.001): valor promedio estimado de CD4 en el tiempo basal (T0), en el grupo de referencia (no tratados, mujeres).

Tiempo (β = 17.7, p < 0.001): cada mes se asocia con un aumento significativo de ~18 células CD4 en promedio → efecto del tiempo positivo y significativo.

Tratamiento (β = 11.9, p = 0.28): los tratados tienen, en promedio, 12 CD4 más que los no tratados, aunque esta diferencia no es estadísticamente significativa.

Sexo (β = -7.8, p = 0.73): los hombres tendrían ~8 CD4 menos que las mujeres, pero tampoco es significativo.

Respuesta a la consigna:

¿Efecto del tiempo (magnitud del cambio por mes)? Sí, significativo → +17.7 CD4/mes (p<0.001).

¿Efecto del tratamiento? En este modelo marginal, la diferencia promedio entre grupos (tratados vs no tratados) no es significativa (p=0.28), aunque la tendencia favorece al tratamiento.

¿Diferencia basal de CD4 entre los grupos? La diferencia basal no resulta significativa al ajustar en el modelo (capturada por el coeficiente de “Treatment”).

📌 Conclusión:

13. Gráfico la trayectoria de los valores de CD4 en tratados y no tratados.

mod_gee4 <- geeglm(cd4 ~ time*Treatment + sex ,
                     data = CD4_Ejercicio1, 
                     id = CD4_Ejercicio1$id,
                     corstr = "unstructured")
tidy(mod_gee4, conf.int = T) 
## # A tibble: 5 × 7
##   term           estimate std.error statistic p.value conf.low conf.high
##   <chr>             <dbl>     <dbl>     <dbl>   <dbl>    <dbl>     <dbl>
## 1 (Intercept)      544.       53.5   104.     0         440.       649. 
## 2 time               9.42      3.65    6.64   0.00998     2.25      16.6
## 3 Treatment        -63.6      37.9     2.81   0.0935   -138.        10.7
## 4 sex               -7.06     49.1     0.0207 0.886    -103.        89.1
## 5 time:Treatment    13.2       5.29    6.23   0.0126      2.83      23.6

Gráfico del cambio de CD4 en tratados y no tratados.

predichosTTO <- data.frame(na.omit(CD4_Ejercicio1), TTO = fitted(mod_gee4))

ggplot(predichosTTO, aes(time, TTO, colour = factor(Treatment))) + 
 geom_smooth(method = "lm") + theme_classic()+ ggtitle("Efectos marginales") +
 labs(x = "Tiempo", y = "CD4") 
## `geom_smooth()` using formula = 'y ~ x'

Resultados del modelo (mod_gee4):

Interpretación:

Conclusión: