# --- CONFIGURACIÓN INICIAL ---
# Este chunk realiza la configuración inicial: limpia el entorno, carga paquetes y establece opciones

# Limpiar entorno (opcional pero recomendado para comenzar desde cero)
rm(list = ls())

# Cargar librerías con conflicto controlado
library(readr)     # Lectura eficiente de datos
library(ggplot2)   # Visualizaciones avanzadas
library(emmeans)   # Medias marginales y comparaciones post-hoc
library(multcomp)  # Letras de significancia
library(multcompView) # Visualización de comparaciones múltiples
library(knitr)     # Reportes reproducibles y tablas formateadas
library(tibble)    # Dataframes mejorados
library(car)       # Análisis de varianza y supuestos
library(DHARMa)    # Diagnóstico de modelos mixtos
library(dplyr)     # Manipulación de datos (cargar al final para evitar conflictos)

Carga y preparación de datos

# --- 1. CARGA Y PREPARACIÓN DE DATOS ---
# Este chunk carga los datos crudos y realiza la transformación inicial

# Leer datos desde texto
hplc <- readr::read_csv("C:/Users/User/Documents/Documentos_Tesis/Estadistica/Factorial_simple_2.csv")

# Procesamiento inicial de datos
hplc <- hplc %>%
  mutate(
    Chakra = factor(Chakra),
    Age = factor(Edades, levels = c("T0", "T1", "T2")), # Factor ordenado
    Light = factor(Condiciones_luz),
    logCGA = log(CGA)  # Transformación logarítmica
  ) %>%
  dplyr::select(Chakra, Age, Light, CGA, logCGA)

# Inspección inicial de datos
print("--- Estructura de Datos Inicial ---")
## [1] "--- Estructura de Datos Inicial ---"
print(head(hplc))
## # A tibble: 6 × 5
##   Chakra Age   Light   CGA logCGA
##   <fct>  <fct> <fct> <dbl>  <dbl>
## 1 A      T0    -      5.69   1.74
## 2 A      T0    -      5.45   1.70
## 3 A      T0    -      5.45   1.70
## 4 A      T0    +     12.6    2.53
## 5 A      T0    +     12.0    2.48
## 6 A      T0    +     13.2    2.58
print(str(hplc))
## tibble [54 × 5] (S3: tbl_df/tbl/data.frame)
##  $ Chakra: Factor w/ 3 levels "A","B","C": 1 1 1 1 1 1 1 1 1 1 ...
##  $ Age   : Factor w/ 3 levels "T0","T1","T2": 1 1 1 1 1 1 2 2 2 2 ...
##  $ Light : Factor w/ 2 levels "-","+": 1 1 1 2 2 2 1 1 1 2 ...
##  $ CGA   : num [1:54] 5.69 5.45 5.45 12.57 11.96 ...
##  $ logCGA: num [1:54] 1.74 1.7 1.7 2.53 2.48 ...
## NULL
print(summary(hplc))
##  Chakra Age     Light       CGA             logCGA      
##  A:18   T0:18   -:27   Min.   : 1.352   Min.   :0.3016  
##  B:18   T1:18   +:27   1st Qu.: 2.447   1st Qu.:0.8950  
##  C:18   T2:18          Median : 3.606   Median :1.2825  
##                        Mean   : 4.770   Mean   :1.3562  
##                        3rd Qu.: 5.669   3rd Qu.:1.7350  
##                        Max.   :13.158   Max.   :2.5770
print("---------------------------------")
## [1] "---------------------------------"

Modelado estadístico

# --- 2. AJUSTE DEL MODELO LINEAL GENERALIZADO (GLM) --->
# Modelo final elegido basado en análisis previo: Gamma con enlace log para logCGA
modelo_final_glm <- glm(logCGA ~ Chakra + Age * Light,
                        family = Gamma(link = "log"),
                        data = hplc)

# Resumen del modelo ajustado
 print("--- Resumen del Modelo GLM ---")
## [1] "--- Resumen del Modelo GLM ---"
 print(summary(modelo_final_glm))
## 
## Call:
## glm(formula = logCGA ~ Chakra + Age * Light, family = Gamma(link = "log"), 
##     data = hplc)
## 
## Coefficients:
##              Estimate Std. Error t value Pr(>|t|)    
## (Intercept)   0.47087    0.06935   6.790 1.89e-08 ***
## ChakraB       0.27529    0.06006   4.584 3.50e-05 ***
## ChakraC      -0.57663    0.06006  -9.601 1.46e-12 ***
## AgeT1        -0.06505    0.08494  -0.766 0.447703    
## AgeT2        -0.19548    0.08494  -2.302 0.025942 *  
## Light+        0.30371    0.08494   3.576 0.000835 ***
## AgeT1:Light+ -0.43341    0.12012  -3.608 0.000757 ***
## AgeT2:Light+ -1.05124    0.12012  -8.752 2.37e-11 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for Gamma family taken to be 0.03246434)
## 
##     Null deviance: 14.0706  on 53  degrees of freedom
## Residual deviance:  1.4713  on 46  degrees of freedom
## AIC: -4.2309
## 
## Number of Fisher Scoring iterations: 6
 print("-----------------------------")
## [1] "-----------------------------"
# Obtener tabla ANOVA del GLM (Tipo III)
 print("--- Tabla ANOVA (Tipo III) del GLM ---")
## [1] "--- Tabla ANOVA (Tipo III) del GLM ---"
 anova_results_glm <- car::Anova(modelo_final_glm, type = "III") # Especificar paquete car::
 print(anova_results_glm)
## Analysis of Deviance Table (Type III tests)
## 
## Response: logCGA
##           LR Chisq Df Pr(>Chisq)    
## Chakra     195.187  2  < 2.2e-16 ***
## Age          5.383  2  0.0677700 .  
## Light       12.726  1  0.0003606 ***
## Age:Light   76.479  2  < 2.2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
 print("------------------------------------")
## [1] "------------------------------------"

Validación de supuestos

# --- 3. EVALUACIÓN DE SUPUESTOS DEL MODELO FINAL --->
# Simular residuos escalados cuantílicos
# Usar tryCatch para manejar posibles errores en la simulación misma
simulationOutput <- tryCatch({
  DHARMa::simulateResiduals(fittedModel = modelo_final_glm, plot = FALSE) # Usar DHARMa:: explícitamente
}, error = function(e) {
  message("Error al simular residuos DHARMa: ", e$message)
  return(NULL)
})

# --- 3.1. Tabla Resumen de Supuestos --->
# Inicializar variables como character para evitar errores de tipo
ks_stat_chr <- ks_pval_chr <- disp_stat_chr <- disp_pval_chr <- out_stat_chr <- out_pval_chr <- "N/A"
ks_interp <- disp_interp <- out_interp <- "Error en simulación DHARMa"

# Proceder solo si la simulación fue exitosa
if (!is.null(simulationOutput)) {
  # Realizar pruebas específicas usando DHARMa:: explícitamente
  test_ks <- DHARMa::testUniformity(simulationOutput, plot = FALSE)
  test_disp <- DHARMa::testDispersion(simulationOutput, plot = FALSE)
  test_out <- DHARMa::testOutliers(simulationOutput, plot = FALSE)

  # Extraer valores numéricos primero
  ks_stat_val <- ifelse(is.numeric(test_ks$statistic), round(test_ks$statistic, 3), NA_real_)
  ks_pval_val <- ifelse(is.numeric(test_ks$p.value), round(test_ks$p.value, 3), NA_real_)
  disp_stat_raw <- test_disp$statistic # Puede no ser un único número
  disp_pval_val <- ifelse(is.numeric(test_disp$p.value), round(test_disp$p.value, 3), NA_real_)
  out_stat_raw <- test_out$statistic # Puede no ser un único número
  out_pval_val <- ifelse(is.numeric(test_out$p.value), round(test_out$p.value, 3), NA_real_)

  # Convertir a character para la tabla, manejando NA y posibles vectores
  ks_stat_chr <- ifelse(is.na(ks_stat_val), "NA", as.character(ks_stat_val))
  ks_pval_chr <- ifelse(is.na(ks_pval_val), "NA", as.character(ks_pval_val))
  disp_stat_chr <- ifelse(is.numeric(disp_stat_raw),
                          ifelse(is.na(disp_stat_raw), "NA", as.character(round(disp_stat_raw, 3))),
                          paste(disp_stat_raw, collapse=";")) # Maneja si no es numérico
  disp_pval_chr <- ifelse(is.na(disp_pval_val), "NA", as.character(disp_pval_val))
   out_stat_chr <- ifelse(is.numeric(out_stat_raw),
                          ifelse(is.na(out_stat_raw), "NA", as.character(round(out_stat_raw, 3))),
                          paste(out_stat_raw, collapse=";")) # Maneja si no es numérico
  out_pval_chr <- ifelse(is.na(out_pval_val), "NA", as.character(out_pval_val))

  # Interpretaciones basadas en valores numéricos antes de convertir a character
  ks_interp <- ifelse(is.na(ks_pval_val), "Error en test KS", ifelse(ks_pval_val < 0.05, "Desviación significativa", "OK"))
  disp_interp <- ifelse(is.na(disp_pval_val), "Error en test Dispersion", ifelse(disp_pval_val < 0.05, "Dispersión no constante/incorrecta", "OK"))
  out_interp <- ifelse(is.na(out_pval_val), "Error en test Outliers", ifelse(out_pval_val < 0.05, "Presencia de Outliers", "OK"))

} else {
   # Asegurar tipo character si la simulación falló
   ks_stat_chr <- ks_pval_chr <- disp_stat_chr <- disp_pval_chr <- out_stat_chr <- out_pval_chr <- "Error Sim."
}


# Obtener VIF (esto ya devuelve carácter o error como carácter)
# Nota: VIF se calcula sobre un modelo aditivo para evaluar colinealidad entre predictores principales
vif_values <- tryCatch({
  # Usar un modelo solo con efectos principales para VIF
  # No incluir interacciones en el cálculo de VIF usualmente
  model_additive_for_vif <- update(modelo_final_glm, . ~ Chakra + Age + Light)
  vif_raw <- car::vif(model_additive_for_vif) # Especificar paquete car::
  # Asegurarse que vif_raw es un vector o matriz nombrada
  if (is.matrix(vif_raw)) { # Si hay múltiples columnas de VIF (e.g., gvif)
      # Podrías querer seleccionar una columna específica o mostrar todo
      # Aquí, simplemente lo convertimos a un formato de texto simple
      vif_text <- paste(rownames(vif_raw), apply(round(vif_raw, 2), 1, paste, collapse=":"), sep=": ", collapse="; ")
  } else if (is.vector(vif_raw) && !is.null(names(vif_raw))) { # Vector nombrado estándar
      vif_text <- paste(names(vif_raw), round(vif_raw, 2), sep=": ", collapse="; ")
  } else {
      vif_text <- "Formato VIF no esperado"
  }
  vif_text
}, error = function(e) {
  message("Error al calcular VIF: ", e$message)
  "Error al calcular VIF"
})
vif_interp <- ifelse(grepl("Error|no esperado", vif_values), "Error/No calculado", "VIFs bajos (<5) indican baja colinealidad")
vif_pval_chr <- "NA" # VIF no tiene p-valor


# Crear la tabla de supuestos (todas las columnas de valores son ahora character)
assumption_summary <- tibble::tribble(
  ~Supuesto, ~Método, ~`Estadístico/Valor`, ~`p-valor`, ~Interpretación,
  # --- DHARMa tests ---
  "Distribución Residuos (QQ-plot)", "DHARMa KS test", ks_stat_chr, ks_pval_chr, ks_interp,
  "Homocedasticidad/Dispersión", "DHARMa testDispersion", disp_stat_chr, disp_pval_chr, disp_interp,
  "Outliers", "DHARMa testOutliers", out_stat_chr, out_pval_chr, out_interp,
  # --- Multicolinealidad (aproximada) ---
  "Multicolinealidad (VIF aditivo)", "car::vif", vif_values, vif_pval_chr, vif_interp
)

# Usar kable para formatear la tabla
 print(knitr::kable(assumption_summary, caption = "Tabla de Supuestos del Modelo GLM Final (logCGA ~ Chakra + Age * Light, Gamma(log))"))
## 
## 
## Table: Tabla de Supuestos del Modelo GLM Final (logCGA ~ Chakra + Age * Light, Gamma(log))
## 
## |Supuesto                        |Método                |Estadístico/Valor                       |p-valor |Interpretación                            |
## |:-------------------------------|:---------------------|:---------------------------------------|:-------|:-----------------------------------------|
## |Distribución Residuos (QQ-plot) |DHARMa KS test        |0.121                                   |0.407   |OK                                        |
## |Homocedasticidad/Dispersión     |DHARMa testDispersion |1.215                                   |0.408   |OK                                        |
## |Outliers                        |DHARMa testOutliers   |0                                       |1       |OK                                        |
## |Multicolinealidad (VIF aditivo) |car::vif              |Chakra: 1:2:1; Age: 1:2:1; Light: 1:1:1 |NA      |VIFs bajos (<5) indican baja colinealidad |
# --- 3.2. Gráficos de Diagnóstico DHARMa --->

# Esencial revisar estos gráficos visualmente además de la tabla
# Proceder solo si la simulación fue exitosa
if (!is.null(simulationOutput)) {plot(simulationOutput) # Esto genera el QQ-plot y el de Residuos vs Predichos
  } else {
     message("No se pueden generar gráficos DHARMa debido a error en la simulación.")
 }

Análisis post-hoc

# --- 4. ANÁLISIS POST-HOC ---
# Usar emmeans sobre el modelo final para obtener medias y comparaciones
# Nota: Las comparaciones se hacen sobre la escala del predictor (log-link en este caso)

 print("--- Comparaciones Post-Hoc (emmeans) ---")
## [1] "--- Comparaciones Post-Hoc (emmeans) ---"
# --- 4.1. Efecto Principal: Chakra --->
emm_chakra <- emmeans::emmeans(modelo_final_glm, ~ Chakra) # Usar emmeans::
pairs_chakra <- pairs(emm_chakra, adjust = "tukey") # Ajuste Tukey para comparaciones
cld_chakra <- multcomp::cld(emm_chakra, Letters = letters, alpha = 0.05, adjust = "tukey") # Usar multcomp::
 print("Comparaciones para Chakra:")
## [1] "Comparaciones para Chakra:"
 print(pairs_chakra)
##  contrast estimate     SE df t.ratio p.value
##  A - B      -0.275 0.0601 46  -4.584  0.0001
##  A - C       0.577 0.0601 46   9.601  <.0001
##  B - C       0.852 0.0601 46  14.185  <.0001
## 
## Results are averaged over the levels of: Age, Light 
## Results are given on the log (not the response) scale. 
## P value adjustment: tukey method for comparing a family of 3 estimates
 print(cld_chakra)
##  Chakra emmean     SE df lower.CL upper.CL .group
##  C      -0.288 0.0425 46   -0.393   -0.183  a    
##  A       0.288 0.0425 46    0.183    0.394   b   
##  B       0.564 0.0425 46    0.459    0.669    c  
## 
## Results are averaged over the levels of: Age, Light 
## Results are given on the log (not the response) scale. 
## Confidence level used: 0.95 
## Conf-level adjustment: sidak method for 3 estimates 
## P value adjustment: tukey method for comparing a family of 3 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.
# --- 4.2. Efecto Principal: Age --->
# Advertencia: Interpretar con cautela debido a la interacción significativa
emm_age <- emmeans::emmeans(modelo_final_glm, ~ Age)
pairs_age <- pairs(emm_age, adjust = "tukey")
cld_age <- multcomp::cld(emm_age, Letters = letters, alpha = 0.05, adjust = "tukey")
 print("Comparaciones para Age (promediado sobre Light - CUIDADO por interacción):")
## [1] "Comparaciones para Age (promediado sobre Light - CUIDADO por interacción):"
 print(pairs_age)
##  contrast estimate     SE df t.ratio p.value
##  T0 - T1     0.282 0.0601 46   4.691  0.0001
##  T0 - T2     0.721 0.0601 46  12.006  <.0001
##  T1 - T2     0.439 0.0601 46   7.315  <.0001
## 
## Results are averaged over the levels of: Chakra, Light 
## Results are given on the log (not the response) scale. 
## P value adjustment: tukey method for comparing a family of 3 estimates
 print(cld_age)
##  Age emmean     SE df lower.CL upper.CL .group
##  T2  -0.199 0.0425 46   -0.304  -0.0936  a    
##  T1   0.241 0.0425 46    0.135   0.3458   b   
##  T0   0.522 0.0425 46    0.417   0.6275    c  
## 
## Results are averaged over the levels of: Chakra, Light 
## Results are given on the log (not the response) scale. 
## Confidence level used: 0.95 
## Conf-level adjustment: sidak method for 3 estimates 
## P value adjustment: tukey method for comparing a family of 3 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.
# --- 4.3. Interacción: Age * Light --->
# Comparar Age dentro de cada nivel de Light
emm_age_light <- emmeans::emmeans(modelo_final_glm, ~ Age | Light)
pairs_age_light <- pairs(emm_age_light, adjust = "sidak") # Sidak para 3 comps por grupo
cld_age_light <- multcomp::cld(emm_age_light, Letters = letters, alpha = 0.05, adjust = "sidak")
 print("Comparaciones para Age dentro de Light:")
## [1] "Comparaciones para Age dentro de Light:"
 print(pairs_age_light)
## Light = -:
##  contrast estimate     SE df t.ratio p.value
##  T0 - T1     0.065 0.0849 46   0.766  0.8315
##  T0 - T2     0.195 0.0849 46   2.302  0.0758
##  T1 - T2     0.130 0.0849 46   1.536  0.3448
## 
## Light = +:
##  contrast estimate     SE df t.ratio p.value
##  T0 - T1     0.498 0.0849 46   5.869  <.0001
##  T0 - T2     1.247 0.0849 46  14.678  <.0001
##  T1 - T2     0.748 0.0849 46   8.810  <.0001
## 
## Results are averaged over the levels of: Chakra 
## Results are given on the log (not the response) scale. 
## P value adjustment: sidak method for 3 tests
 print(cld_age_light)
## Light = -:
##  Age emmean     SE df lower.CL upper.CL .group
##  T2   0.175 0.0601 46   0.0261    0.324  a    
##  T1   0.305 0.0601 46   0.1566    0.454  a    
##  T0   0.370 0.0601 46   0.2216    0.519  a    
## 
## Light = +:
##  Age emmean     SE df lower.CL upper.CL .group
##  T2  -0.573 0.0601 46  -0.7214   -0.424  a    
##  T1   0.176 0.0601 46   0.0269    0.324   b   
##  T0   0.674 0.0601 46   0.5253    0.823    c  
## 
## Results are averaged over the levels of: Chakra 
## Results are given on the log (not the response) scale. 
## Confidence level used: 0.95 
## Conf-level adjustment: sidak method for 3 estimates 
## P value adjustment: sidak method for 3 tests 
## 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.
# O Comparar Light dentro de cada nivel de Age
emm_light_age <- emmeans::emmeans(modelo_final_glm, ~ Light | Age)
pairs_light_age <- pairs(emm_light_age, adjust = "sidak") # Ajuste para 3 comparaciones
 print("Comparaciones para Light dentro de Age:")
## [1] "Comparaciones para Light dentro de Age:"
 print(pairs_light_age)
## Age = T0:
##  contrast  estimate     SE df t.ratio p.value
##  (-) - (+)   -0.304 0.0849 46  -3.576  0.0008
## 
## Age = T1:
##  contrast  estimate     SE df t.ratio p.value
##  (-) - (+)    0.130 0.0849 46   1.527  0.1336
## 
## Age = T2:
##  contrast  estimate     SE df t.ratio p.value
##  (-) - (+)    0.748 0.0849 46   8.801  <.0001
## 
## Results are averaged over the levels of: Chakra 
## Results are given on the log (not the response) scale.
# Crear símbolos de significancia para el gráfico t-test style
 signif_light_age <- as.data.frame(pairs_light_age) %>%
   dplyr::mutate(significance = case_when( # Usar dplyr::
            p.value <= 0.001 ~ "***",
            p.value <= 0.01  ~ "**",
            p.value <= 0.05  ~ "*",
            TRUE             ~ "ns" # No Significativo
          ))

Visualización de resultados

# --- 5. VISUALIZACIÓN DE RESULTADOS ---
# Este chunk genera las figuras finales para el informe

# --- 5.1. Preparación para Gráficos --->
# *** FUNCIÓN AUXILIAR CORREGIDA (sin cambios respecto a la versión anterior) ***
add_cld_letters <- function(summary_df, cld_object, group_vars) {
  # Convertir el objeto cld (puede ser emmGrid) a data.frame
  if (!is.data.frame(cld_object)) {
    cld_df <- as.data.frame(cld_object)
  } else {
    cld_df <- cld_object
  }

  # Verificar que las columnas de agrupación y .group existen en cld_df
  required_cols <- c(group_vars, ".group")
  if (!all(required_cols %in% names(cld_df))) {
    stop("Las columnas requeridas (", paste(required_cols, collapse=", "), ") no se encuentran todas en el objeto cld.")
  }

  # Limpiar espacios en blanco de las letras de grupo
  cld_df$.group <- trimws(cld_df$.group)

  # Seleccionar solo las columnas necesarias de cld_df
  # Usar all_of() para seguridad si group_vars viene de una variable externa
  cld_subset <- cld_df %>%
                  dplyr::select(all_of(group_vars), .group) # Usar dplyr::

  # Asegurarse que las columnas de unión sean del mismo tipo (factor es seguro)
  for (var in group_vars) {
      if (var %in% names(summary_df) && var %in% names(cld_subset)) {
          summary_df[[var]] <- as.factor(summary_df[[var]])
          cld_subset[[var]] <- as.factor(cld_subset[[var]])
      } else {
          stop(paste("La columna de agrupación", var, "no existe en summary_df o cld_subset."))
      }
  }

  # Unir los datos resumen con las letras CLD
  dplyr::left_join(summary_df, cld_subset, by = group_vars) # Usar dplyr::
}

Grafico chakra

# --- 5.2. Gráfico: Efecto Principal Chakra --->
# print("--- Generando Gráfico: Efecto Principal Chakra ---")
# Calcular directamente sobre hplc agrupado solo por Chakra
summary_chakra_direct <- hplc %>% dplyr::group_by(Chakra) %>% # Usar dplyr::
    # Usar dplyr::summarise explícitamente
    dplyr::summarise(mean_CGA = mean(CGA), sd_CGA = sd(CGA), N=n(), se_CGA = sd_CGA/sqrt(N), .groups='drop')

# Añadir letras usando la función CORREGIDA
summary_chakra_plot <- add_cld_letters(summary_chakra_direct, cld_chakra, "Chakra")

# Verificar el resultado antes de graficar
#print(summary_chakra_plot)

# Definir colores manualmente (cambia los valores hex a tus colores deseados)
colores_manual <- c("#4457A5FF", "#B24422FF","#4F9D4EFF")

# Definir etiquetas personalizadas para el eje X (en el orden de tus niveles de Chakra)
etiquetas_x <- c("Talag", "Alto Pano", "Alto Tena")

plot_chakra <- ggplot2::ggplot(summary_chakra_plot, ggplot2::aes(x = Chakra, y = mean_CGA, fill = Chakra)) +
  ggplot2::geom_col(position = ggplot2::position_dodge(width = 0.9), color = "black") +
  ggplot2::geom_errorbar(ggplot2::aes(ymin = mean_CGA - se_CGA, ymax = mean_CGA + se_CGA),
                width = 0.2, position = ggplot2::position_dodge(width = 0.9)) +
  ggplot2::geom_text(ggplot2::aes(label = .group, y = mean_CGA + se_CGA), vjust = -0.5, size = 4) +
  
  # Cambiar colores manualmente
  ggplot2::scale_fill_manual(values = colores_manual) +
  
  # Cambiar etiquetas del eje X
  ggplot2::scale_x_discrete(labels = etiquetas_x) +
  
  ggplot2::labs(title = "Effect of Chakra on CGA",
       x = "Chakra",
       y = "CGA Mean ± SD",
       fill = "Chakra") +
  ggplot2::theme_minimal(base_size = 14) +
  ggplot2::theme(legend.position = "none")

print(plot_chakra)

Grafico de las edades

# --- 6.1. Gráfico: Efecto Principal Age ---
# Calcular resumen agrupando solo por Age
summary_age_direct <- hplc %>%
  dplyr::group_by(Age) %>%
  dplyr::summarise(
    mean_CGA = mean(CGA, na.rm = TRUE),
    sd_CGA = sd(CGA, na.rm = TRUE),
    N = n(),
    se_CGA = sd_CGA / sqrt(N),
    .groups = 'drop'
   )

# Añadir letras CLD (calculadas previamente en cld_age)
# Nota: CLD se basa en logCGA, el gráfico muestra CGA. Interpretar con cautela.
summary_age_plot <- add_cld_letters(summary_age_direct, cld_age, "Age")

# Definir colores manualmente para Age (modifica los códigos hex)
colores_manual_edad <- c("#E76BF3", "#00B0F6", "#F8766D")  # Debe coincidir con el número de niveles de Age

# Definir etiquetas personalizadas para el eje X (en el orden de tus niveles de Age)
etiquetas_x_edad <- c("4-6 years", "6-8 years", "8-10 years")  # Ejemplo en español, modifica según necesites

plot_age <- ggplot2::ggplot(
    data = summary_age_plot,
    mapping = ggplot2::aes(x = Age, y = mean_CGA, fill = Age)
  ) +
  ggplot2::geom_col(
    position = ggplot2::position_dodge(width = 0.9), 
    color = "black",
    show.legend = FALSE
  ) +
  ggplot2::geom_errorbar(
    ggplot2::aes(ymin = mean_CGA - se_CGA, ymax = mean_CGA + se_CGA),
    width = 0.2,
    position = ggplot2::position_dodge(width = 0.9)
  ) +
  ggplot2::geom_text(
    ggplot2::aes(label = .group, y = mean_CGA + se_CGA),
    vjust = -0.5, 
    size = 4
  ) +
  # Aplicar personalizaciones
  ggplot2::scale_fill_manual(values = colores_manual_edad) +  # Colores manuales
  ggplot2::scale_x_discrete(labels = etiquetas_x_edad) +      # Etiquetas personalizadas en eje X
  ggplot2::labs(
    title = "Effect of Age on CGA",
    #subtitle = "(Promediado sobre Chakra y Light; CLD basado en logCGA)",
    x = "Age",
    y = "CGA Mean ± SD"
  ) +
  ggplot2::theme_minimal(base_size = 14)

print(plot_age)

Grafico interaccion de la luz:edad

# --- 6.3. Gráficos de Interacción Separados por Luz ---
# *** PASO 1: Calcular medias y SE promediando sobre Chakra ***
# Agrupar por Age y Light para obtener medias consistentes con las CLD
summary_stats_avg <- hplc %>%
  dplyr::group_by(Age, Light) %>% # Usar dplyr::
  # Usar dplyr::summarise explícitamente
  dplyr::summarise(
    N = n(), # Nota: N aquí es el número de Chakras * réplicas por Chakra
    mean_CGA = mean(CGA, na.rm = TRUE),
    sd_CGA = sd(CGA, na.rm = TRUE),
    # Calcular SE sobre la media de las medias de Chakra o directamente sobre los datos
    # Aquí lo calculamos sobre todos los datos dentro de Age:Light
    se_CGA = sd_CGA / sqrt(N),
    .groups = 'drop' # Evita que siga agrupado
  )

# *** PASO 2: Añadir letras CLD para la interacción (cld_age_light) ***
# Necesitamos unir por Age y Light al data frame promediado
summary_interaction_plot_avg <- add_cld_letters(summary_stats_avg, cld_age_light, c("Age", "Light"))

# Usar summary_interaction_plot_avg que ya tiene las medias promediadas sobre Chakra
# y las letras CLD para Age dentro de cada Light
summary_dark <- summary_interaction_plot_avg %>% dplyr::filter(Light == "-")
# Definir colores manuales para Age (oscuros/acordes a la temática de oscuridad)
colores_oscuridad <- c("#32373AFF", "#32373AFF", "#32373AFF")  # 3 tonos de azul oscuro

# Definir etiquetas personalizadas para el eje X (mismo orden que los niveles de Age)
etiquetas_x_oscuridad <- c("4-6 years", "6-8 years", "8-10 years")  # Ejemplo

plot_interaction_dark <- ggplot2::ggplot(
  summary_dark, 
  ggplot2::aes(x = Age, y = mean_CGA, fill = Age)  # Mapear fill a Age para usar colores manuales
) +
  ggplot2::geom_col(color = "black", show.legend = FALSE) +  # Quitamos el fill fijo
  ggplot2::geom_errorbar(
    ggplot2::aes(ymin = mean_CGA - se_CGA, ymax = mean_CGA + se_CGA),
    width = 0.2
  ) +
  ggplot2::geom_text(
    ggplot2::aes(label = .group, y = mean_CGA + se_CGA),
    vjust = -0.5, 
    size = 4
  ) +
  # Aplicar personalizaciones
  ggplot2::scale_fill_manual(values = colores_oscuridad) +  # Colores manuales
  ggplot2::scale_x_discrete(labels = etiquetas_x_oscuridad) +  # Etiquetas personalizadas
  ggplot2::labs(
    title = "Age Effect in Shadow",
    #subtitle = "Medias ± Error Estándar (promediado sobre Chakra)",
    x = "Age",
    y = "CGA Mean ± SD"
  ) +
  ggplot2::theme_minimal(base_size = 14) +
  ggplot2::coord_cartesian(
    ylim = c(0, max(summary_interaction_plot_avg$mean_CGA + 
                    summary_interaction_plot_avg$se_CGA) * 1.1)
  )

print(plot_interaction_dark)

# Gráfico para Light = "+" (Luz)
summary_light_cond <- summary_interaction_plot_avg %>% dplyr::filter(Light == "+")

# Definir colores manuales para Age (tonos claros/acordes a la temática de luz)
colores_luz <- c("#F5BC5CFF", "#F5BC5CFF", "#F5BC5CFF")

# Definir etiquetas personalizadas para el eje X (mismo orden que los niveles de Age)
etiquetas_x_luz <- c("4-6 years", "6-8 years", "8-10 years")  # Ejemplo en español

plot_interaction_light <- ggplot2::ggplot(
  summary_light_cond, 
  ggplot2::aes(x = Age, y = mean_CGA, fill = Age)  # Mapear fill a Age
) +
  ggplot2::geom_col(color = "black", show.legend = FALSE) +  # Quitamos el fill fijo
  ggplot2::geom_errorbar(
    ggplot2::aes(ymin = mean_CGA - se_CGA, ymax = mean_CGA + se_CGA),
    width = 0.2
  ) +
  ggplot2::geom_text(
    ggplot2::aes(label = .group, y = mean_CGA + se_CGA),
    vjust = -0.5, 
    size = 4
  ) +
  # Aplicar personalizaciones
  ggplot2::scale_fill_manual(values = colores_luz) +  # Colores manuales
  ggplot2::scale_x_discrete(labels = etiquetas_x_luz) +  # Etiquetas personalizadas
  ggplot2::labs(
    title = "Age Effect with Light",
    #subtitle = "Medias ± Error Estándar (promediado sobre Chakra)",
    x = "Age",
    y = "CGA Mean ± SD"
  ) +
  ggplot2::theme_minimal(base_size = 14) +
  ggplot2::coord_cartesian(
    ylim = c(0, max(summary_interaction_plot_avg$mean_CGA + 
                    summary_interaction_plot_avg$se_CGA) * 1.1)
  )

print(plot_interaction_light)

Light:Age - T-test

# --- 6.4. Gráfico de Interacción Combinado con Resultados T-test (Light vs Dark) ---
# Preparar datos de significancia para el gráfico
# Usar pairs_light_age calculado previamente
signif_light_age_df <- as.data.frame(pairs_light_age) %>%
  dplyr::mutate(significance = case_when(
           p.value <= 0.001 ~ "***",
           p.value <= 0.01  ~ "**",
           p.value <= 0.05  ~ "*",
           TRUE             ~ "ns" # No Significativo
         ))

# Necesitamos calcular posiciones Y para los símbolos de significancia
# Calcularemos la posición Y máxima para cada Age y añadiremos un offset
y_pos_signif <- summary_interaction_plot_avg %>%
  dplyr::group_by(Age) %>%
  dplyr::summarise(max_y = max(mean_CGA + se_CGA), .groups = "drop") %>%
  dplyr::mutate(y_position = max_y * 1.05) # Poner el texto un 5% por encima del máximo

# Unir las posiciones Y con los símbolos de significancia
signif_data_plot <- dplyr::left_join(signif_light_age_df, y_pos_signif, by = "Age")

# Crear el gráfico base (similar al plot_interaction anterior, sin letras CLD)
# Definir elementos personalizables --------------------------------------------------
# 1. Colores para las condiciones de Luz (Light)
colores_interaccion <- c(
  "-" = "#32373AFF",   # Oscuridad (azul oscuro)
  "+" = "#F5BC5CFF"    # Luz (dorado)
)

# 2. Etiquetas personalizadas para el eje X (Age)
etiquetas_x_interaccion <- c("4-6 years", "6-8 years", "8-10 years")

# 3. Símbolos de significancia (personaliza si es necesario)
simbolos_significancia <- c("***" = "***", "**" = "**", "*" = "*", "ns" = "ns")

# Crear el gráfico base ------------------------------------------------------------
plot_interaction_ttest_base <- ggplot2::ggplot(
  summary_interaction_plot_avg, 
  ggplot2::aes(x = Age, y = mean_CGA, fill = Light)
) +
  ggplot2::geom_col(
    position = ggplot2::position_dodge(width = 0.9), 
    color = "black"
  ) +
  ggplot2::geom_errorbar(
    ggplot2::aes(ymin = mean_CGA - se_CGA, ymax = mean_CGA + se_CGA),
    width = 0.2, 
    position = ggplot2::position_dodge(width = 0.9)
  ) +
  # Personalizar colores y etiquetas
  ggplot2::scale_fill_manual(
    values = colores_interaccion,
    labels = c("-" = "Shadow", "+" = "Light")  # Puedes cambiar estos textos
  ) +
  ggplot2::scale_x_discrete(labels = etiquetas_x_interaccion) +  # Etiquetas eje X
  ggplot2::labs(
    title = "Age and Light exposure Interaction : Light vs Shadow comparison",
    #subtitle = "Medias ± Error Estándar (promediado sobre Chakra)",
    x = "Age",
    y = "CGA Mean ± SD",
    fill = "Light Condition"
  ) +
  ggplot2::theme_minimal(base_size = 14) +
  ggplot2::theme(legend.position = "top") +
  ggplot2::coord_cartesian(ylim = c(0, max(y_pos_signif$y_position) * 1.1))

# Añadir símbolos de significancia -------------------------------------------------
plot_interaction_ttest <- plot_interaction_ttest_base +
  ggplot2::geom_text(
    data = signif_data_plot,
    ggplot2::aes(x = Age, y = y_position, label = significance),
    inherit.aes = FALSE,
    size = 5, 
    vjust = 0
  )

print(plot_interaction_ttest)

# --- 7. TABLA RESUMEN CON MEDIAS, SD, LETRAS CLD Y P-VALORES ---
# Función para procesar cada factor
process_factor <- function(data, model, factor_name, response_var = "CGA") {
  # Calcular emmeans y CLD
  emm <- emmeans::emmeans(model, specs = as.formula(paste("~", factor_name)))
  cld <- multcomp::cld(emm, Letters = letters, adjust = "tukey")
  
  # Calcular estadísticas descriptivas
  stats <- data %>%
    dplyr::group_by(!!sym(factor_name)) %>%
    dplyr::summarise(
      mean = mean(!!sym(response_var), na.rm = TRUE),
      sd = sd(!!sym(response_var), na.rm = TRUE),
      .groups = 'drop'
    ) %>%
    dplyr::left_join(cld, by = factor_name)
  
  # Obtener p-valor del ANOVA
  anova_table <- car::Anova(model, type = 3)
  p_value <- anova_table[factor_name, "Pr(>Chisq)"]
  
  # Formatear salida
  stats %>%
    dplyr::mutate(
      !!sym("Media ± SD") := sprintf(
        "%.4f ± %.3f%s", 
        round(mean, 4), 
        round(sd, 3), 
        .group
      )
    ) %>%
    dplyr::select(!!sym(factor_name), `Media ± SD`) %>%
    dplyr::rename(Nivel = !!sym(factor_name)) %>%
    dplyr::add_row(
      Nivel = "p-value*", 
      `Media ± SD` = ifelse(
        p_value < 0.0001, 
        "< 0.0001", 
        sprintf("%.4f", p_value)
      )
    ) %>%
    dplyr::mutate(Factor = factor_name) %>%
    dplyr::select(Factor, Nivel, `Media ± SD`)
}

# Aplicar a cada factor
final_table <- purrr::map_dfr(
  c("Chakra", "Age", "Light"),
  ~ process_factor(hplc, modelo_final_glm, .x)
)

# Formatear tabla final
final_table <- final_table %>%
  dplyr::mutate(
    Factor = dplyr::case_when(
      duplicated(Factor) ~ "",
      TRUE ~ Factor
    )
  ) %>%
  dplyr::rename(
    ` ` = Factor,
    `Tratamiento` = Nivel,
    `Media ± SD (CGA)` = `Media ± SD`
  )

# Mostrar tabla con formato
knitr::kable(
  final_table,
  caption = "Tabla Resumen: Medias ± SD con grupos de Tukey y p-valores",
  align = c('l', 'l', 'r'),
  col.names = c("Factor", "Nivel", "Media ± SD (CGA)")
)
Tabla Resumen: Medias ± SD con grupos de Tukey y p-valores
Factor Nivel Media ± SD (CGA)
Chakra A 5.2377 ± 3.644 b
B 6.7256 ± 2.838 c
C 2.3459 ± 0.710 a
p-value* < 0.0001
Age T0 6.8723 ± 3.633 c
T1 4.0122 ± 1.360 b
T2 3.4247 ± 3.176 a
p-value* 0.0678
Light - 4.7047 ± 2.640 b
+ 4.8347 ± 3.763 a
p-value* 0.0004

Limpieza opcional final

# --- 6. LIMPIEZA FINAL ---
# Este chunk opcional puede usarse para eliminar objetos temporales
# rm(list = ls(pattern = "^temp_"))