Taller de Diseño de Experimentos — Punto 1 (Suelo/Biomasa), Punto 2 (Moluscos) y Punto 3 (Biodiversidad de anfibios)

Punto 1 – Suelo y producción de biomasa

Planteamiento

Para estudiar la relación entre ciertas características del suelo y la producción de biomasa (g) de una planta forrajera natural se obtuvieron 45 muestras en diferentes ambientes. En cada una se estimó la biomasa (variable respuesta, \(Y\)) y se registraron cuatro características del suelo (covariables, \(X\)): pH, Salinidad, Zinc y Potasio.

a. Análisis exploratorio univariado

table1(~ Biomasa + pH + Salinidad + Zinc + Potasio, data = Salinidad)
Overall
(N=45)
Biomasa
Mean (SD) 1080 (546)
Median [Min, Max] 992 [370, 2340]
pH
Mean (SD) 4.61 (1.25)
Median [Min, Max] 4.45 [3.20, 7.45]
Salinidad
Mean (SD) 30.3 (3.72)
Median [Min, Max] 30.0 [24.0, 38.0]
Zinc
Mean (SD) 17.8 (8.27)
Median [Min, Max] 19.2 [0.211, 31.3]
Potasio
Mean (SD) 797 (298)
Median [Min, Max] 773 [351, 1440]
largo <- pivot_longer(Salinidad, everything(),
                       names_to = "variable", values_to = "valor")

ggplot(largo, aes(x = valor)) +
  geom_histogram(bins = 10, fill = "#2E86AB", color = "white") +
  facet_wrap(~ variable, scales = "free") +
  theme_minimal(base_family = "sans") +
  labs(title = "Distribución de cada variable", x = NULL, y = "Frecuencia") +
  theme(plot.title = element_text(hjust = 0.5, face = "bold"))

ggplot(largo, aes(y = valor)) +
  geom_boxplot(fill = "#AED6F1", color = "#1a5276") +
  facet_wrap(~ variable, scales = "free") +
  theme_minimal(base_family = "sans") +
  labs(title = "Boxplots por variable", y = NULL) +
  theme(plot.title = element_text(hjust = 0.5, face = "bold"),
        axis.text.x = element_blank())

vars <- c("Biomasa", "pH", "Salinidad", "Zinc", "Potasio")

resumen_uni <- data.frame(
  Variable = vars,
  `Shapiro-Wilk (W)` = sapply(Salinidad[vars], function(x) shapiro.test(x)$statistic),
  `Valor p` = sapply(Salinidad[vars], function(x) {
    p <- shapiro.test(x)$p.value
    formatC(p, format = "f", digits = 8)
  }),
  `CV (%)` = sapply(Salinidad[vars], function(x) sd(x) / mean(x) * 100),
  check.names = FALSE
)

tabla_bonita(resumen_uni, caption = "Prueba de normalidad y coeficiente de variación por variable")
Prueba de normalidad y coeficiente de variación por variable
Variable Shapiro-Wilk (W) Valor p CV (%)
Biomasa.W Biomasa 0.897 0.00077847 50.481
pH.W pH 0.882 0.00027797 27.224
Salinidad.W Salinidad 0.950 0.05074950 12.290
Zinc.W Zinc 0.930 0.00913530 46.404
Potasio.W Potasio 0.922 0.00502078 37.319
  • Biomasa: Media 1082 g y mediana 992 g, con alta variabilidad (CV ≈ 50%). La distribución es asimétrica a la derecha, es decir, la mayoría de las muestras se concentra entre 6c50 y 1350 g, y solo unas pocas que superan 2100 g. El test de Shapiro-Wilk (p = 0.00077847) muestra que no es normal.
  • pH: El pH en general es ácido (mediana 4.45), con asimetría positiva. Hay un grupo mayoritario con pH ≤ 5.6 y un pequeño grupo con pH ≥ 7.1 que alarga la cola derecha. No es normal (p = 0.00027797).
  • Salinidad: Es la variable más homogénea (CV ≈ 12%), con media y mediana casi iguales (30.3 y 30). Es la única que se acerca a la normalidad (p = 0.05074), justo en el límite.
  • Zinc: El zinc tiene asimetría negativa por la presencia de cinco valores muy bajos (0.21 a 0.37), aislados del resto de las observaciones. El boxplot los marca como atípicos. No es normal (p = 0.00913).
  • Potasio: Media 797 y mediana 773, rango 351–1442, asimetría moderada. Se rechaza normalidad (p = 0.00502).

Solo Salinidad se acerca a una distribución normal, las demás variables muestran asimetría, y hay cinco valores atípicos de pH y Zinc que merecen atención especial, pues corresponden a las mismas cinco observaciones.

b. Análisis exploratorio bivariado

Se evaluó la relación entre la biomasa y las covariables pH, Salinidad y Zinc, tanto de forma gráfica como numérica (correlación y regresión simple).

Salinidad$pH_g  <- cut(Salinidad$pH, breaks = 3, labels = c("Bajo", "Medio", "Alto"))
Salinidad$Sal_g <- cut(Salinidad$Salinidad, breaks = 3, labels = c("Bajo", "Medio", "Alto"))
Salinidad$Zn_g  <- cut(Salinidad$Zinc, breaks = 3, labels = c("Bajo", "Medio", "Alto"))

table1(~ Biomasa | pH_g, data = Salinidad)
Bajo
(N=26)
Medio
(N=14)
Alto
(N=5)
Overall
(N=45)
Biomasa
Mean (SD) 751 (258) 1280 (287) 2260 (74.3) 1080 (546)
Median [Min, Max] 710 [370, 1200] 1330 [765, 1900] 2270 [2160, 2340] 992 [370, 2340]
table1(~ Biomasa | Sal_g, data = Salinidad)
Bajo
(N=16)
Medio
(N=19)
Alto
(N=10)
Overall
(N=45)
Biomasa
Mean (SD) 1090 (421) 1050 (607) 1120 (650) 1080 (546)
Median [Min, Max] 1150 [466, 1900] 896 [370, 2340] 888 [497, 2330] 992 [370, 2340]
table1(~ Biomasa | Zn_g, data = Salinidad)
Bajo
(N=7)
Medio
(N=21)
Alto
(N=17)
Overall
(N=45)
Biomasa
Mean (SD) 1960 (524) 1070 (342) 735 (325) 1080 (546)
Median [Min, Max] 2220 [1190, 2340] 1010 [546, 1900] 612 [370, 1410] 992 [370, 2340]
ggplot(Salinidad, aes(x = pH, y = Biomasa)) +
  geom_point(color = "#2c3e50", alpha = 0.75) +
  geom_smooth(color = "#C0392B", fill = "#f5b7b1") +
  theme_minimal(base_family = "sans") +
  labs(title = "Biomasa vs pH") +
  theme(plot.title = element_text(hjust = 0.5, face = "bold"))

ggplot(Salinidad, aes(x = Salinidad, y = Biomasa)) +
  geom_point(color = "#2c3e50", alpha = 0.75) +
  geom_smooth(color = "#C0392B", fill = "#f5b7b1") +
  theme_minimal(base_family = "sans") +
  labs(title = "Biomasa vs Salinidad") +
  theme(plot.title = element_text(hjust = 0.5, face = "bold"))

ggplot(Salinidad, aes(x = Zinc, y = Biomasa)) +
  geom_point(color = "#2c3e50", alpha = 0.75) +
  geom_smooth(color = "#C0392B", fill = "#f5b7b1") +
  theme_minimal(base_family = "sans") +
  labs(title = "Biomasa vs Zinc") +
  theme(plot.title = element_text(hjust = 0.5, face = "bold"))

resumen_bi <- do.call(rbind, lapply(c("pH", "Salinidad", "Zinc"), function(v) {
  ct <- cor.test(Salinidad$Biomasa, Salinidad[[v]])
  mod <- lm(reformulate(v, "Biomasa"), data = Salinidad)
  data.frame(
    Covariable = v,
    r = unname(ct$estimate),
    `Valor p` = ct$p.value,
    `R²` = summary(mod)$r.squared,
    Pendiente = unname(coef(mod)[2]),
    check.names = FALSE
  )
}))

tabla_bonita(resumen_bi, caption = "Correlación de Pearson y regresión simple: Biomasa vs. cada covariable")
Correlación de Pearson y regresión simple: Biomasa vs. cada covariable
Covariable r Valor p R² Pendiente
pH 0.928 0.000 0.861 404.079
Salinidad -0.067 0.664 0.004 -9.778
Zinc -0.781 0.000 0.611 -51.595
  • pH: La producción de biomasa aumenta si el pH aumenta, es una relación directamente proporcional muy fuerte (r = 0.93) y el pH por sí solo explica el 86% de la variabilidad de la biomasa. Es la covariable con mayor relación.
  • Zinc: La biomasa disminuye si la concentración de Zinc aumenta, es una relación inversamente proporcional, r = -0.78. Se observa un rango entre 15 y 20 aproximadamente donde la biomasa se mantiene más estable, lo que sugiere una forma no del todo lineal. Es la segunda covariable en importancia.
  • Salinidad: Se mantiene prácticamente constante frente a la biomasa, no se evidencia posible relación (p = 0.664, R² ≈ 0).

Ranking de relación con la biomasa: pH > Zinc > Salinidad.

pH y Zinc están correlacionados entre sí (r = -0.72). Las cinco observaciones con pH ≥ 7.1 son justamente las de menor Zinc y mayor biomasa, y sostienen buena parte de la relación de Zinc con la biomasa. pH conserva el r más alto.

c. ANOVA de una vía sobre el pH categorizado

Categorización

Como pH presentó la mayor relación con la biomasa, se categorizó en tres niveles usando terciles, lo que garantiza grupos balanceados (15 observaciones cada uno):

q <- quantile(Salinidad$pH, probs = c(1/3, 2/3))
Salinidad$pH_cat <- cut(Salinidad$pH,
                         breaks = c(-Inf, q[1], q[2], Inf),
                         labels = c("Bajo", "Medio", "Alto"))
Salinidad$pH_cat <- as.factor(Salinidad$pH_cat)

tabla_bonita(as.data.frame(table(Salinidad$pH_cat)) |>
               setNames(c("Nivel de pH", "n")),
             caption = "Número de observaciones por nivel de pH", digits = 0)
Número de observaciones por nivel de pH
Nivel de pH n
Bajo 15
Medio 15
Alto 15

ANOVA de una vía

modelo_sal <- aov(Biomasa ~ pH_cat, data = Salinidad)

tabla_bonita(tidy(modelo_sal), caption = "Tabla ANOVA: Biomasa ~ Nivel de pH")
Tabla ANOVA: Biomasa ~ Nivel de pH
term df sumsq meansq statistic p.value
pH_cat 2 7712683 3856341.6 29.893 0
Residuals 42 5418235 129005.6 NA NA

El nivel de pH genera diferencias significativas en la biomasa (F(2, 42) = 29.89, p < 0.001).

Verificación de supuestos

sh_sal <- shapiro.test(residuals(modelo_sal))
lv_sal <- leveneTest(Biomasa ~ pH_cat, data = Salinidad)

supuestos_sal <- data.frame(
  Supuesto = c("Normalidad de residuales (Shapiro-Wilk)",
               "Homogeneidad de varianzas (Levene)"),
  Estadístico = c(unname(sh_sal$statistic), lv_sal$`F value`[1]),
  `Valor p` = c(sh_sal$p.value, lv_sal$`Pr(>F)`[1]),
  `¿Se cumple?` = c("Sí", "No"),
  check.names = FALSE
)

tabla_bonita(supuestos_sal, caption = "Verificación de supuestos del modelo")
Verificación de supuestos del modelo
Supuesto Estadístico Valor p ¿Se cumple?
Normalidad de residuales (Shapiro-Wilk) 0.973 0.375 Sí
Homogeneidad de varianzas (Levene) 8.675 0.001 No
  • Normalidad de residuales: p = 0.375 → no se rechaza; los residuales son consistentes con una distribución normal. Se cumple.
  • Homogeneidad de varianzas: p = 0.0007 → se rechaza; las varianzas no son homogéneas entre los tres grupos (la desviación estándar de la biomasa crece de 165 en el nivel Bajo a 548 en el nivel Alto). No se cumple.

Comparaciones post-hoc (LSD)

resultado_sal <- LSD.test(modelo_sal, "pH_cat", console = TRUE)
grupos_lsd <- resultado_sal$groups
grupos_lsd$Nivel <- rownames(grupos_lsd)
grupos_lsd <- grupos_lsd[, c("Nivel", "Biomasa", "groups")]
names(grupos_lsd) <- c("Nivel de pH", "Media Biomasa", "Grupo")

tabla_bonita(grupos_lsd, caption = paste0("Prueba LSD (Fisher). LSD crítico = ",
                                           round(resultado_sal$statistics$LSD, 2)))
Prueba LSD (Fisher). LSD crítico = 264.67
Nivel de pH Media Biomasa Grupo
Alto Alto 1605.385 a
Medio Medio 1048.109 b
Bajo Bajo 593.023 c

Los tres niveles de pH obtuvieron letras distintas (a, b, c), lo que indica que los tres difieren significativamente entre sí. no hay ningún par de niveles con biomasa estadísticamente igual. La biomasa aumenta de forma consistente con el nivel de pH (Alto > Medio > Bajo), y las tres diferencias de medias (1012.4, 557.3 y 455.1) superan la diferencia mínima significativa (LSD = 264.67).

Conclusión Punto 1

La biomasa de la planta forrajera está fuertemente asociada con el pH del suelo y, en menor medida, con la concentración de Zinc, mientras que la Salinidad no muestra relación relevante. Al categorizar el pH en tres niveles, el ANOVA confirmó diferencias significativas en la biomasa entre ellos, con un patrón claro de mayor biomasa a mayor pH.


Punto 2 – Consumo de oxigeno en moluscos

Planteamiento

Se midió el consumo de oxígeno (proporción de O₂ por unidad de peso seco) en dos tipos de moluscos (A y B) sometidos a tres concentraciones de agua de mar (100 %, 75 % y 50 %). El diseño es completamente balanceado: 8 observaciones por cada una de las 6 combinaciones, para un total de 48 unidades experimentales.

tabla_desc <- data.frame(
  VARIABLE = c("cons_o", "molusco", "c_agua"),
  TIPO     = c("Cuantitativa continua", "Cualitativa (factor)", "Cualitativa (factor)"),
  ESCALA   = c("Razón", "Nominal", "Ordinal"),
  `NIVELES / RANGO` = c(
    "1.80 – 18.80",
    "A (24), B (24)",
    "50 % (16), 75 % (16), 100 % (16)"
  ),
  check.names = FALSE)

gt(tabla_desc) %>%
  tab_header(title = md("**Descripción de variables**")) %>%
  cols_align(align = "left") %>%
  tab_style(
    style = cell_fill(color = "#eaf2f8"),
    locations = cells_column_labels()
  ) %>%
  tab_style(
    style = list(cell_fill(color = "#fbfcfd"),
                 cell_text(color = "#1a5276", weight = "bold")),
    locations = cells_body(columns = VARIABLE)
  ) %>%
  tab_options(table.font.size = 13)
Descripción de variables
VARIABLE TIPO ESCALA NIVELES / RANGO
cons_o Cuantitativa continua Razón 1.80 – 18.80
molusco Cualitativa (factor) Nominal A (24), B (24)
c_agua Cualitativa (factor) Ordinal 50 % (16), 75 % (16), 100 % (16)

a. Análisis exploratorio univariado

Variables cualitativas

Ambos factores están perfectamente balanceados, 24 observaciones por tipo de molusco y 16 por concentración, con 8 réplicas en cada celda. Este balance es deseable porque hace que las sumas de cuadrados de los efectos sean ortogonales (no dependen del orden en que se incluyan en el modelo) y otorga máxima potencia al contraste, además de dar robustez frente a desviaciones moderadas de los supuestos.

Variables cuantitativas

p1 <- ggplot(BD, aes(x = cons_o)) +
  geom_histogram(bins = 10, fill = "#2E86AB", color = "white") +
  geom_vline(xintercept = mean(BD$cons_o),   color = "#C0392B") +
  geom_vline(xintercept = median(BD$cons_o), color = "darkgreen",
             linetype = "dashed") +
  labs(title = "Distribución consumo de oxígeno",
       x = "Consumo de O2", y = "Frecuencia") +
  theme_minimal(base_family = "sans") +
  theme(plot.title = element_text(hjust = 0.5, face = "bold"))

p2 <- ggplot(BD, aes(y = cons_o)) +
  geom_boxplot(fill = "#AED6F1", color = "#1a5276") +
  labs(y = "Consumo de O2") +
  theme_minimal(base_family = "sans") +
  theme(axis.text.x = element_blank(), axis.ticks.x = element_blank())

grid.newpage()
pushViewport(viewport(layout = grid.layout(1, 2,
                                           widths = unit(c(3, 2), "null"))))
print(p1, vp = viewport(layout.pos.row = 1, layout.pos.col = 1))
print(p2, vp = viewport(layout.pos.row = 1, layout.pos.col = 2))

Figura 1. Distribución del consumo de oxígeno para las 48 observaciones, ignorando los factores. La media (línea roja) y la mediana (línea punteada) casi coinciden.

mi_render <- function(x, ...) {
  x <- x[!is.na(x)]
  c(
    "Media (DE)"       = sprintf("%.2f (%.2f)", mean(x), sd(x)),
    "Mediana [Q1, Q3]" = sprintf("%.2f [%.2f, %.2f]",
                                 median(x),
                                 quantile(x, 0.25, names = FALSE),
                                 quantile(x, 0.75, names = FALSE)),
    "Mín, Máx"         = sprintf("%.2f, %.2f", min(x), max(x)),
    "CV (%)"           = sprintf("%.1f", 100 * sd(x) / mean(x))
  )
}

table1(~ cons_o + c_agua + molusco, data = BD)
Overall
(N=48)
Consumo de O2
Mean (SD) 9.30 (3.68)
Median [Min, Max] 9.70 [1.80, 18.8]
Concentración de agua de mar
50% 16 (33.3%)
75% 16 (33.3%)
100% 16 (33.3%)
Tipo de molusco
A 24 (50.0%)
B 24 (50.0%)
table1(~ cons_o, data = BD,
       render.continuous = mi_render,
       render.missing = NULL)
Overall
(N=48)
Consumo de O2 9.30 (3.68)
Mediana [Q1, Q3] 9.70 [6.31, 11.23]
Mín, Máx 1.80, 18.80
CV (%) 39.6

La media (9.30) y la mediana (9.70) son similares, indicando una distribución simétrica con alta variabilidad (CV = 39.6%). Se detecta un atípico (18.80) que se conserva por no ser un valor biológicamente imposible. El diseño está perfectamente balanceado: 24/24 por molusco y 16/16/16 por concentración.

b. Análisis exploratorio bivariado

tabla_conc <- BD %>%
  group_by(Concentración = c_agua) %>%
  summarise(
    N       = n(),
    Media   = mean(cons_o),
    Mediana = median(cons_o),
    D.E.    = sd(cons_o),
    Mín     = min(cons_o),
    Máx     = max(cons_o)
  )

tabla_bonita(tabla_conc, caption = "Resumen del consumo de O2 por concentración de agua de mar")
Resumen del consumo de O2 por concentración de agua de mar
Concentración N Media Mediana D.E. Mín Máx
50% 16 12.251 11.455 3.200 6.38 18.8
75% 16 6.992 6.430 2.804 1.80 13.2
100% 16 8.671 8.595 3.001 3.68 14.0
tabla_molusco <- BD %>%
  group_by(Molusco = molusco) %>%
  summarise(
    N       = n(),
    Media   = mean(cons_o),
    Mediana = median(cons_o),
    D.E.    = sd(cons_o),
    Mín     = min(cons_o),
    Máx     = max(cons_o)
  )

tabla_bonita(tabla_molusco, caption = "Resumen del consumo de O2 por tipo de molusco")
Resumen del consumo de O2 por tipo de molusco
Molusco N Media Mediana D.E. Mín Máx
A 24 10.000 9.74 3.269 5.2 18.8
B 24 8.609 8.06 4.002 1.8 17.7
p3 <- ggplot(BD, aes(x = c_agua, y = cons_o, fill = c_agua)) +
  geom_boxplot(alpha = 0.8) +
  geom_jitter(width = 0.08, size = 1.3, alpha = 0.6) +
  scale_fill_manual(values = c("#AED6F1", "#5DADE2", "#1a5276")) +
  labs(x = "Concentración de agua de mar", y = "Consumo de O2") +
  theme_minimal(base_family = "sans") + theme(legend.position = "none")

p4 <- ggplot(BD, aes(x = molusco, y = cons_o, fill = molusco)) +
  geom_boxplot(alpha = 0.8) +
  geom_jitter(width = 0.08, size = 1.3, alpha = 0.6) +
  scale_fill_manual(values = c("#AED6F1", "#1a5276")) +
  labs(x = "Tipo de molusco", y = "Consumo de O2") +
  theme_minimal(base_family = "sans") + theme(legend.position = "none")

grid.newpage()
pushViewport(viewport(layout = grid.layout(1, 2,
                                           widths = unit(c(3, 2), "null"))))
print(p3, vp = viewport(layout.pos.row = 1, layout.pos.col = 1))
print(p4, vp = viewport(layout.pos.row = 1, layout.pos.col = 2))

Figura 2. Consumo de oxígeno por concentración de agua de mar (izquierda) y por tipo de molusco (derecha). Los puntos son las observaciones individuales.

El consumo no varía de forma lineal con la concentración, es máximo al 50% (12.25), mínimo al 75% (6.99) y repunta al 100% (8.67), posiblemente por estrés osmótico en agua diluida. La diferencia entre moluscos es pequeña (A=10.00, B=8.61). El gráfico de interacción muestra líneas paralelas entre A y B, sugiriendo que ambos responden igual a la salinidad.

Consumo por concentración dentro de cada tipo de molusco

tabla_celdas <- BD %>%
  group_by(Molusco = molusco, Concentración = c_agua) %>%
  summarise(
    N       = n(),
    Media   = mean(cons_o),
    Mediana = median(cons_o),
    D.E.    = sd(cons_o),
    .groups = "drop"
  ) %>%
  mutate(`CV (%)` = 100 * D.E. / Media)

tabla_bonita(tabla_celdas, caption = "Resumen del consumo de O2 por combinación molusco × concentración")
Resumen del consumo de O2 por combinación molusco × concentración
Molusco Concentración N Media Mediana D.E. CV (%)
A 50% 8 12.175 11.110 3.090 25.381
A 75% 8 7.890 7.180 2.740 34.722
A 100% 8 9.936 9.295 2.748 27.656
B 50% 8 12.326 12.850 3.518 28.540
B 75% 8 6.095 5.595 2.739 44.940
B 100% 8 7.406 6.140 2.844 38.401
ggplot(BD, aes(x = c_agua, y = cons_o, fill = molusco)) +
  geom_boxplot(alpha = 0.85) +
  scale_fill_manual(values = c("#AED6F1", "#1a5276")) +
  labs(title = "Consumo de O2 en las seis combinaciones factor × factor",
       x = "Concentración de agua de mar", y = "Consumo de O2",
       fill = "Molusco") +
  theme_minimal(base_family = "sans") +
  theme(plot.title = element_text(hjust = 0.5, face = "bold"))

Figura 3. Consumo de oxígeno en las seis combinaciones factor × factor.

ggplot(BD, aes(x = c_agua, y = cons_o, color = molusco, group = molusco)) +
  stat_summary(fun = mean, geom = "point", size = 2.5) +
  stat_summary(fun = mean, geom = "line", linewidth = 1) +
  stat_summary(fun.data = mean_se, geom = "errorbar", width = 0.1) +
  scale_color_manual(values = c("#C0392B", "#1a5276")) +
  labs(title = "Gráfico de interacción: media de cada celda ± error estándar",
       x = "Concentración de agua de mar", y = "Consumo medio de O2",
       color = "Molusco") +
  theme_minimal(base_family = "sans") +
  theme(plot.title = element_text(hjust = 0.5, face = "bold"))

Figura 4. Gráfico de interacción con la media de cada celda ± error estándar.

El gráfico de interacción muestra dos perfiles con la misma forma, en ambos tipos el consumo alcanza su máximo al 50 %, cae al mínimo al 75 % y repunta ligeramente al 100 %. Las líneas son prácticamente paralelas y no se cruzan, lo cual sugiere ausencia de interacción, el efecto de la salinidad sobre el consumo de oxígeno opera en la misma dirección y con magnitud similar en los dos moluscos.

Los coeficientes de variación dentro de las celdas oscilan entre 25 % y 45 %, sin un patrón de crecimiento asociado a la media, lo cual es un primer indicio favorable para el supuesto de homogeneidad de varianzas.

c. ANOVA de dos vías con interacción

Modelo

Se contrastan tres juegos de hipótesis, efecto principal del tipo de molusco, efecto principal de la concentración y efecto de interacción. El orden correcto de lectura es de abajo hacia arriba: primero la interacción, porque si resultara significativa, los efectos principales no serían interpretables por separado.

modelo_mol <- aov(cons_o ~ molusco * c_agua, data = BD)
anova_raw <- summary(modelo_mol)[[1]]

tabla_anova <- data.frame(
  Fuente = c("Tipo de molusco", "Concentración de agua",
             "Molusco × Concentración", "Error (residual)", "Total"),
  SC = c(anova_raw$`Sum Sq`, sum(anova_raw$`Sum Sq`)),
  GL = c(anova_raw$Df, sum(anova_raw$Df)),
  CM = c(anova_raw$`Mean Sq`, NA),
  F  = c(anova_raw$`F value`[1:3], NA, NA),
  `Valor p` = c(anova_raw$`Pr(>F)`[1:3], NA, NA),
  check.names = FALSE
)

tabla_fmt <- tabla_anova
tabla_fmt$CM <- ifelse(is.na(tabla_fmt$CM), "—", sprintf("%.3f", tabla_fmt$CM))
tabla_fmt$F  <- ifelse(is.na(tabla_fmt$F), "—", sprintf("%.3f", tabla_fmt$F))
tabla_fmt$`Valor p` <- ifelse(is.na(tabla_fmt$`Valor p`), "—",
                              ifelse(tabla_fmt$`Valor p` < 0.0001, "< 0.0001",
                                     sprintf("%.4f", tabla_fmt$`Valor p`)))
tabla_fmt$SC <- sprintf("%.3f", tabla_fmt$SC)

tabla_bonita(tabla_fmt, caption = "Tabla de análisis de varianza (dos vías con interacción)")
Tabla de análisis de varianza (dos vías con interacción)
Fuente SC GL CM F Valor p
Tipo de molusco 23.227 1 23.227 2.651 0.1110
Concentración de agua 230.816 2 115.408 13.171 < 0.0001
Molusco × Concentración 15.356 2 7.678 0.876 0.4238
Error (residual) 368.011 42 8.762 — —
Total 637.410 47 — — —

La interacción molusco×concentración no es significativa (p=0.424), por lo que los efectos principales se interpretan por separado. El tipo de molusco tampoco resulta significativo (p=0.111). La concentración sí lo es (p<0.0001), indicando que al menos un nivel difiere de los demás.

Verificación de supuestos

res <- residuals(modelo_mol)

diag <- data.frame(ajustados = fitted(modelo_mol), residuales = res)

ggplot(diag, aes(x = ajustados, y = residuales)) +
  geom_point(color = "#2c3e50", alpha = 0.75) +
  geom_hline(yintercept = 0, color = "#C0392B") +
  labs(title = "Residuales vs. valores ajustados",
       x = "Valores ajustados", y = "Residuales") +
  theme_minimal(base_family = "sans") +
  theme(plot.title = element_text(hjust = 0.5, face = "bold"))

ggplot(diag, aes(sample = residuales)) +
  stat_qq(color = "#2c3e50", alpha = 0.75) +
  stat_qq_line(color = "#C0392B") +
  labs(title = "Gráfico Q-Q de los residuales",
       x = "Cuantiles teóricos", y = "Cuantiles muestrales") +
  theme_minimal(base_family = "sans") +
  theme(plot.title = element_text(hjust = 0.5, face = "bold"))

ggplot(diag, aes(x = residuales)) +
  geom_histogram(bins = 10, fill = "#AED6F1", color = "#1a5276") +
  labs(title = "Histograma de residuales", x = "Residual", y = "Frecuencia") +
  theme_minimal(base_family = "sans") +
  theme(plot.title = element_text(hjust = 0.5, face = "bold"))

Figura 5. Diagnóstico del modelo: residuales frente a valores ajustados, gráfico cuantil-cuantil e histograma de residuales.

sh_mol <- shapiro.test(res)
bt_mol <- bartlett.test(cons_o ~ interaction(molusco, c_agua), data = BD)

tabla_supuestos_mol <- data.frame(
  Supuesto = c("Normalidad de residuales", "Homogeneidad de varianzas"),
  Prueba = c("Shapiro–Wilk", "Bartlett"),
  Estadístico = c(sprintf("W = %.3f", sh_mol$statistic),
                  sprintf("χ² = %.3f", bt_mol$statistic)),
  `Valor p` = c(sprintf("%.4f", sh_mol$p.value), sprintf("%.4f", bt_mol$p.value)),
  `Decisión (α = 0.05)` = ifelse(c(sh_mol$p.value, bt_mol$p.value) > 0.05,
                                 "No se rechaza H₀", "Se rechaza H₀"),
  check.names = FALSE
)

tabla_bonita(tabla_supuestos_mol, caption = "Verificación de supuestos del modelo (dos vías)")
Verificación de supuestos del modelo (dos vías)
Supuesto Prueba Estadístico Valor p Decisión (α = 0.05)
Normalidad de residuales Shapiro–Wilk W = 0.958 0.0857 No se rechaza H₀
Homogeneidad de varianzas Bartlett χ² = 0.712 0.9823 No se rechaza H₀

Ambos supuestos se cumplen: normalidad de residuales (Shapiro-Wilk, p=0.086) y homogeneidad de varianzas (Bartlett, p=0.982), ambos con p>0.05.

Interpretación de los efectos

1. Interacción molusco × concentración (F2,42 = 0.876; p = 0.4238). No significativa. No hay evidencia de que el efecto de la concentración de agua de mar dependa del tipo de molusco. Esto valida cuantitativamente lo observado en la Figura 3, los perfiles de respuesta son paralelos. En consecuencia, los efectos principales sí pueden interpretarse de forma independiente y el modelo puede leerse como aditivo.

2. Tipo de molusco (F1,42 = 2.651; p = 0.1110). No significativo. Aunque el molusco A consumió en promedio 1.39 unidades más de oxígeno que el B, esa diferencia no supera lo que cabría esperar por azar dada la variabilidad dentro de los grupos. No hay evidencia de que los dos tipos de moluscos difieran en su consumo de oxígeno. Al no ser significativo, no procede realizar comparaciones post-hoc para este factor (y además, con solo dos niveles, la prueba F ya es en sí misma la comparación entre ambos).

3. Concentración de agua de mar (F2,42 = 13.171; p < 0.0001). Altamente significativo. Existe evidencia muy fuerte de que al menos una de las tres concentraciones produce un consumo de oxígeno medio distinto de las demás. Este factor por sí solo explica el 36.2 % de la variabilidad total (230.8 de 637.4). Como la prueba F es global y no indica cuáles niveles difieren, se procede con las comparaciones múltiples.

Comparaciones post-hoc: prueba LSD de Fisher

La diferencia mínima significativa se calcula con el cuadrado medio del error del modelo completo y sus 42 grados de libertad, sobre las medias marginales de concentración (n = 16 por nivel):

CME <- summary(modelo_mol)[[1]]["Residuals", "Mean Sq"]
gl  <- df.residual(modelo_mol)
n_por_nivel <- 16
LSD <- qt(0.975, gl) * sqrt(2 * CME / n_por_nivel)

medias <- tapply(BD$cons_o, BD$c_agua, mean)
se <- sqrt(2 * CME / n_por_nivel)

pares <- combn(names(medias), 2, function(par) {
  dif <- medias[par[1]] - medias[par[2]]
  t   <- dif / se
  p   <- 2 * (1 - pt(abs(t), gl))
  data.frame(
    Comparación = paste(par[1], "−", par[2]),
    `Diferencia de medias` = round(dif, 3),
    `Dif vs. LSD` = paste0(round(abs(dif), 3),
                             ifelse(abs(dif) > LSD, " > ", " < "), round(LSD, 3)),
    `IC 95%` = sprintf("(%.3f ; %.3f)", dif - LSD, dif + LSD),
    `Valor p` = ifelse(p < 0.0001, "< 0.0001", sprintf("%.4f", p)),
    Conclusión = ifelse(abs(dif) > LSD, "Difieren", "No difieren"),
    check.names = FALSE
  )
}, simplify = FALSE)

tabla_comparaciones <- do.call(rbind, pares)

tabla_bonita(tabla_comparaciones, caption = "Comparaciones pareadas (LSD) entre concentraciones de agua de mar")
Comparaciones pareadas (LSD) entre concentraciones de agua de mar
Comparación Diferencia de medias Dif vs. LSD IC 95% Valor p Conclusión
50% 50% − 75% 5.258 5.258 > 2.112 (3.146 ; 7.370) < 0.0001 Difieren
50%1 50% − 100% 3.579 3.579 > 2.112 (1.467 ; 5.691) 0.0014 Difieren
75% 75% − 100% -1.679 1.679 < 2.112 (-3.791 ; 0.433) 0.1162 No difieren
lsd_conc <- LSD.test(modelo_mol, "c_agua", console = FALSE)

tabla_grupos_mol <- data.frame(
  Concentración = rownames(lsd_conc$groups),
  Media = round(lsd_conc$groups$cons_o, 3),
  `Error estándar` = round(se, 3),
  `Grupo LSD` = lsd_conc$groups$groups,
  check.names = FALSE
)

tabla_bonita(tabla_grupos_mol, caption = "Grupos LSD por concentración de agua de mar")
Grupos LSD por concentración de agua de mar
Concentración Media Error estándar Grupo LSD
50% 12.251 1.047 a
100% 8.671 1.047 b
75% 6.992 1.047 b

El 50% forma un grupo propio, con consumo significativamente mayor que el 75% (p<0.0001) y el 100% (p=0.0014). El 75% y el 100% no difieren entre sí (p=0.116).

Conclusión Punto 2

La concentración de agua de mar afecta significativamente el consumo de oxígeno (p < 0.0001), mientras que el tipo de molusco no lo hace (p = 0.111) y no existe interacción entre ambos factores (p = 0.424).

El consumo es marcadamente superior en agua diluida al 50 % (media 12.25) frente al 75 % (6.99) y al 100 % (8.67), que entre sí no se diferencian. El efecto no es lineal ni monótono: el mínimo se da en la concentración intermedia.

Al no haber interacción, este comportamiento es el mismo para los dos tipos de moluscos, de modo que las conclusiones sobre el efecto de la salinidad pueden generalizarse a ambos sin distinción.


Punto 3 – Biodiversidad de anfibios

Planteamiento

Con el fin de evaluar el efecto del uso del suelo sobre la biodiversidad de anfibios en una reserva forestal, se establecieron parcelas de muestreo en cuatro tipos de hábitat que representan un gradiente de intervención antrópica: bosque primario, bosque secundario, sistema silvopastoril y potrero. En cada una de las 52 parcelas (13 por hábitat) se registró la riqueza de especies (número de especies observadas), el índice de diversidad de Shannon–Wiener y la altitud (m.s.n.m.) del sitio de muestreo.

a. Análisis exploratorio univariado

Tabla de medidas descriptivas
table1(~ Riqueza + Shannon + Altitud, data = BD_biodiversidad)
Overall
(N=52)
Riqueza
Mean (SD) 9.73 (4.37)
Median [Min, Max] 9.50 [2.00, 21.0]
Shannon
Mean (SD) 1.76 (0.607)
Median [Min, Max] 1.85 [0.670, 2.98]
Altitud
Mean (SD) 1240 (173)
Median [Min, Max] 1250 [951, 1600]
Histogramas de distribución para cada característica
ggplot(BD_biodiversidad, aes(x = Riqueza)) +
  geom_histogram(aes(y = after_stat(density)),
                 bins = 10, fill = "#2E86AB", color = "white") +
  geom_density(color = "#1a5276", linewidth = 1) +
  labs(title = "Distribución de Riqueza",
       x = "Número de especies", y = "Densidad") +
  theme_minimal(base_family = "sans") +
  theme(plot.title = element_text(hjust = 0.5, face = "bold"))

ggplot(BD_biodiversidad, aes(x = Shannon)) +
  geom_histogram(aes(y = after_stat(density)),
                 bins = 10, fill = "#2E86AB", color = "white") +
  geom_density(color = "#1a5276", linewidth = 1) +
  labs(title = "Distribución de Shannon",
       x = "Índice de Shannon", y = "Densidad") +
  theme_minimal(base_family = "sans") +
  theme(plot.title = element_text(hjust = 0.5, face = "bold"))

ggplot(BD_biodiversidad, aes(x = Altitud)) +
  geom_histogram(aes(y = after_stat(density)),
                 bins = 10, fill = "#2E86AB", color = "white") +
  geom_density(color = "#1a5276", linewidth = 1) +
  labs(title = "Distribución de Altitud",
       x = "Altitud (m s. n. m.)", y = "Densidad") +
  theme_minimal(base_family = "sans") +
  theme(plot.title = element_text(hjust = 0.5, face = "bold"))

Diagrama de cajas
largo_bio <- pivot_longer(BD_biodiversidad,
                           cols = c(Riqueza, Shannon, Altitud),
                           names_to = "Variable", values_to = "Valor")

ggplot(largo_bio, aes(x = "", y = Valor)) +
  geom_boxplot(fill = "#AED6F1", color = "#1a5276",
               outlier.color = "#C0392B", outlier.shape = 1, outlier.size = 2) +
  facet_wrap(~ Variable, scales = "free") +
  labs(title = "Boxplot de las variables", x = NULL, y = "Valor") +
  theme_minimal(base_family = "sans") +
  theme(plot.title = element_text(hjust = 0.5, face = "bold"),
        strip.text = element_text(face = "bold", size = 12),
        axis.text.x = element_blank())

  • Riqueza: promedio de 9.73 especies por parcela, con una desviación estándar de 4.37. La mediana fue de 9.5 y el rango va de 2 a 21 especies. La desviación estándar alta indica que la riqueza varía mucho entre parcelas, no todas tienen una cantidad similar de especies. El histograma refuerza esta idea al mostrar una distribución bimodal, con un pico cerca de 6 especies y otro cerca de 14, lo que sugiere que las parcelas se agrupan en dos tipos según sus condiciones. El boxplot no muestra valores atípicos, es decir, todas las parcelas están dentro del rango esperado.
  • Shannon: presentó un promedio de 1.76, con una desviación estándar de 0.61. La mediana fue de 1.85 y el rango va de 0.67 a 2.98. La cercanía entre media y mediana sugiere una distribución aproximadamente simétrica. El histograma muestra forma de campana, con la mayoría de parcelas concentradas entre 1.5 y 2.5, sin la bimodalidad observada en riqueza. El boxplot no muestra atípicos y la caja es compacta, lo que confirma una dispersión moderada.
  • Altitud: tiene un promedio de 1240 m.s.n.m., con una desviación estándar de 173. La mediana fue de 1250 y el rango va de 951 a 1600 m. El histograma presenta dos picos, uno alrededor de 1050 m y otro alrededor de 1350 m, lo que indica que las parcelas no están concentradas en un solo rango altitudinal. El boxplot no muestra valores atípicos y la dispersión relativa es baja comparada con las otras variables.

b. Análisis exploratorio bivariado

Tabla comparativa en los diferentes tipos de hábitat
table1(~ Shannon + Riqueza + Altitud | Habitat, data = BD_biodiversidad)
Bosque primario
(N=13)
Bosque secundario
(N=13)
Potrero
(N=13)
Sistema silvopastoril
(N=13)
Overall
(N=52)
Shannon
Mean (SD) 2.47 (0.377) 1.97 (0.220) 1.00 (0.240) 1.60 (0.281) 1.76 (0.607)
Median [Min, Max] 2.53 [1.73, 2.98] 2.03 [1.44, 2.27] 0.980 [0.670, 1.34] 1.62 [1.14, 2.06] 1.85 [0.670, 2.98]
Riqueza
Mean (SD) 14.8 (2.74) 11.1 (3.01) 5.00 (1.29) 8.08 (2.50) 9.73 (4.37)
Median [Min, Max] 14.0 [10.0, 21.0] 12.0 [3.00, 15.0] 5.00 [2.00, 7.00] 7.00 [5.00, 13.0] 9.50 [2.00, 21.0]
Altitud
Mean (SD) 1450 (77.5) 1330 (40.6) 1040 (50.3) 1150 (97.9) 1240 (173)
Median [Min, Max] 1480 [1320, 1600] 1320 [1250, 1380] 1040 [951, 1120] 1140 [1030, 1390] 1250 [951, 1600]

La tabla muestra un gradiente claro en la diversidad de anfibios: tanto la riqueza como el índice de Shannon disminuyen conforme aumenta la intervención humana. El bosque primario presenta los valores más altos (≈14.8 especies y Shannon ≈2.47), mientras que el potrero tiene los más bajos (≈5.0 especies y Shannon ≈1.0). El bosque secundario y el sistema silvopastoril ocupan posiciones intermedias. Este patrón sugiere que la diversidad de anfibios está fuertemente asociada al tipo de hábitat, y que la pérdida de área boscosa reduce la cantidad y uniformidad de especies.

largo2_bio <- pivot_longer(BD_biodiversidad,
                            cols = c(Riqueza, Shannon),
                            names_to = "Variable", values_to = "Valor")

paleta_habitat <- c(
  "Bosque primario"       = "#1a5276",
  "Bosque secundario"     = "#2E86AB",
  "Sistema silvopastoril" = "#F9A825",
  "Potrero"               = "#A1662F"
)

abrev_habitat <- c(
  "Bosque primario"       = "BP",
  "Bosque secundario"     = "BS",
  "Sistema silvopastoril" = "S",
  "Potrero"               = "P"
)

ggplot(largo2_bio, aes(x = Habitat, y = Valor, fill = Habitat)) +
  geom_boxplot(alpha = 0.85, color = "black") +
  facet_wrap(~ Variable, scales = "free_y") +
  scale_fill_manual(values = paleta_habitat, labels = abrev_habitat) +
  scale_x_discrete(labels = abrev_habitat) +
  labs(title = "Riqueza y Shannon por tipo de hábitat",
       x = NULL, y = "Valor", fill = "Hábitat") +
  theme_minimal(base_family = "sans") +
  theme(plot.title = element_text(hjust = 0.5, face = "bold"),
        strip.text = element_text(face = "bold", size = 12),
        legend.position = "bottom")

El boxplot compara la riqueza y la diversidad de Shannon entre los cuatro tipos de hábitat, y confirma el gradiente: ambas variables disminuyen conforme aumenta la intervención humana.

  • Riqueza: el bosque primario (BP) presenta la mediana más alta, seguido del bosque secundario (BS), el sistema silvopastoril (S) y el potrero (P). Las cajas de BP y BS se ubican claramente por encima de las de S y P, sin traslape entre sí, lo que sugiere diferencias consistentes entre hábitats.
  • Shannon: el patrón es similar: BP tiene la mediana más alta, seguido de BS, S y P. Las cajas tampoco se traslapan, lo que refuerza que la diversidad cambia a lo largo del gradiente de intervención.

Se observan algunos valores atípicos, pero reflejan variabilidad natural dentro de los hábitats y no necesariamente errores de muestreo.

ggplot(BD_biodiversidad, aes(x = Altitud, y = Shannon, color = Habitat)) +
  geom_point(size = 2.5) +
  geom_smooth(method = "lm", se = FALSE, color = "#1a5276") +
  scale_color_manual(values = paleta_habitat, labels = abrev_habitat) +
  labs(title = "Altitud vs. diversidad de Shannon",
       x = "Altitud (m s. n. m.)", y = "Índice de Shannon", color = "Hábitat") +
  theme_minimal(base_family = "sans") +
  theme(plot.title = element_text(hjust = 0.5, face = "bold"),
        legend.position = "bottom")

El gráfico de dispersión muestra una relación positiva entre altitud y Shannon: a mayor altitud, mayor diversidad. Sin embargo, al colorear los puntos por hábitat, se observa que los colores se agrupan por zonas: el bosque primario está arriba y a la derecha, y el potrero está abajo y a la izquierda. Esto sugiere que la relación global no es necesariamente causal, la altitud no causa por sí sola más diversidad, sino que los hábitats más conservados están ubicados a mayor altitud. Es decir, la altitud está confundida con el hábitat.

cor_pearson_bio  <- cor.test(BD_biodiversidad$Altitud, BD_biodiversidad$Shannon)
cor_spearman_bio <- cor.test(BD_biodiversidad$Altitud, BD_biodiversidad$Shannon,
                              method = "spearman", exact = FALSE)

tabla_cor_global <- data.frame(
  Prueba = c("Pearson", "Spearman"),
  r = c(round(cor_pearson_bio$estimate, 3), round(cor_spearman_bio$estimate, 3)),
  `Valor p` = c(format(cor_pearson_bio$p.value, scientific = TRUE, digits = 3),
                format(cor_spearman_bio$p.value, scientific = TRUE, digits = 3)),
  check.names = FALSE
)

tabla_bonita(tabla_cor_global, caption = "Correlación global Altitud - Shannon")
Correlación global Altitud - Shannon
Prueba r Valor p
cor Pearson 0.752 1.29e-10
rho Spearman 0.750 1.56e-10
tabla_cor_habitat <- BD_biodiversidad %>%
  group_by(Habitat) %>%
  summarise(r = cor(Altitud, Shannon),
            `Valor p` = cor.test(Altitud, Shannon)$p.value)

tabla_bonita(tabla_cor_habitat, caption = "Correlación altitud-Shannon dentro de cada hábitat")
Correlación altitud-Shannon dentro de cada hábitat
Habitat r Valor p
Bosque primario -0.328 0.274
Bosque secundario -0.005 0.986
Potrero 0.034 0.912
Sistema silvopastoril -0.513 0.073

Existe una correlación global positiva y significativa entre altitud y Shannon (r ≈ 0.75; p < 0.001). La altitud explica aproximadamente el 56% de la variación en la diversidad, y Spearman confirma el resultado, lo que indica que la relación no depende de valores extremos.

Sin embargo, al calcular la correlación dentro de cada hábitat, la relación altitud-Shannon desaparece: los valores de r son bajos o negativos y ninguno es significativo (todos p > 0.05). Esto confirma que la correlación global era un efecto del hábitat, no de la altitud.

En conclusión, no es que la altitud aumente la diversidad, sino que los hábitats más conservados están ubicados a mayor altitud y, al mismo tiempo, tienen mayor diversidad.

c. ANOVA de una vía y LSD

modelo_bio <- aov(Shannon ~ Habitat, data = BD_biodiversidad)
anova_res_bio <- summary(modelo_bio)[[1]]

tabla_anova_bio <- data.frame(
  Fuente  = rownames(anova_res_bio),
  GL      = anova_res_bio$Df,
  SC      = round(anova_res_bio$`Sum Sq`, 3),
  CM      = round(anova_res_bio$`Mean Sq`, 3),
  F       = round(anova_res_bio$`F value`, 2),
  `Valor p` = format(anova_res_bio$`Pr(>F)`, scientific = TRUE, digits = 3),
  check.names = FALSE
)

tabla_bonita(tabla_anova_bio, caption = "Tabla de análisis de varianza (Shannon ~ Hábitat)")
Tabla de análisis de varianza (Shannon ~ Hábitat)
Fuente GL SC CM F Valor p
Habitat 3 14.862 4.954 60.61 2.38e-16
Residuals 48 3.923 0.082 NA NA

Se evaluó si el índice de Shannon difiere entre los cuatro hábitats. El modelo resultó altamente significativo: F(3, 48) = 60.61; p = 2.38 × 10⁻¹⁶. Se rechaza la hipótesis nula de igualdad de medias, lo que indica que al menos un hábitat difiere de los demás.

res_bio <- residuals(modelo_bio)

shap_bio <- shapiro.test(res_bio)
lev_bio  <- leveneTest(Shannon ~ Habitat, data = BD_biodiversidad)

tabla_supuestos_bio <- data.frame(
  Supuesto = c("Normalidad de residuales (Shapiro-Wilk)",
               "Homogeneidad de varianzas (Levene)"),
  Estadístico = c(round(shap_bio$statistic, 4), round(lev_bio$`F value`[1], 3)),
  `Valor p` = c(format(shap_bio$p.value, digits = 3),
                format(lev_bio$`Pr(>F)`[1], digits = 3)),
  `¿Se cumple?` = c("Sí", "Sí"),
  check.names = FALSE
)

tabla_bonita(tabla_supuestos_bio, caption = "Verificación de supuestos del modelo")
Verificación de supuestos del modelo
Supuesto Estadístico Valor p ¿Se cumple?
W Normalidad de residuales (Shapiro-Wilk) 0.979 0.463 Sí
Homogeneidad de varianzas (Levene) 1.277 0.293 Sí

Los residuales son compatibles con una distribución normal (Shapiro-Wilk: W = 0.978; p = 0.463), y las varianzas son homogéneas entre hábitats (Levene: F = 1.277; p = 0.293). Ambos supuestos se cumplen.

lsd_bio <- LSD.test(modelo_bio, "Habitat", p.adj = "none", console = FALSE)

medias_bio <- lsd_bio$means
letras_bio <- lsd_bio$groups
orden_bio  <- order(-medias_bio$Shannon)
medias_bio <- medias_bio[orden_bio, ]
letras_bio <- letras_bio[orden_bio, ]

tabla_lsd_bio <- data.frame(
  Hábitat = rownames(medias_bio),
  `Media de Shannon` = round(medias_bio$Shannon, 2),
  DE = round(medias_bio$std, 2),
  Grupo = letras_bio$groups,
  check.names = FALSE
)

tabla_bonita(tabla_lsd_bio, caption = "Prueba LSD (Fisher) por tipo de hábitat")
Prueba LSD (Fisher) por tipo de hábitat
Hábitat Media de Shannon DE Grupo
Bosque primario 2.47 0.38 a
Bosque secundario 1.97 0.22 b
Sistema silvopastoril 1.60 0.28 d
Potrero 1.00 0.24 c

Los cuatro hábitats difieren significativamente entre sí (letras distintas): bosque primario (2.47, a) > bosque secundario (1.97, b) > sistema silvopastoril (1.60, c) > potrero (1.00, d). La diferencia mínima significativa fue de 0.225, y todas las diferencias entre pares la superan.

Síntesis

La diversidad de anfibios disminuye de manera escalonada al aumentar la intervención humana. Aunque la altitud se correlaciona con la diversidad a nivel global, esta relación desaparece dentro de cada hábitat, lo que indica que el verdadero factor explicativo es el tipo de hábitat y no la altitud en sí misma.

Conclusión Punto 3

La diversidad de anfibios disminuye de manera escalonada al aumentar la intervención humana. El bosque primario conserva la mayor diversidad, mientras que el potrero presenta la menor. El bosque secundario y el sistema silvopastoril representan estados intermedios, lo que sugiere que la regeneración forestal y los sistemas con cobertura arbórea amortiguan parcialmente la pérdida de diversidad, pero no sustituyen al bosque primario como refugio de anfibios.