Cómo usar este informe. Guarda este .Rmd y tu base (area_foliar.xlsx) en la misma carpeta y pulsa Knit. Si tu archivo se llama distinto, cambia archivo en el encabezado (params). Las tablas, gráficas y los números dentro del texto se recalculan solos; la interpretación hay que revisarla y reescribirla con tus propias palabras. Paquetes: knitr, rmarkdown, readxl (para leer el .xlsx) y, opcionalmente, leaflet (mapa interactivo).

1 Contexto del estudio

1.1 Ficha del estudio

crudo <- leer_base(params$archivo)

esperadas <- c("grupo", "id_arbol", "lat", "lon", "n_hoja", "area_cm2", "exposicion", "fecha", "observaciones")
faltan <- setdiff(esperadas, names(crudo))
if (length(faltan) > 0) stop("Faltan columnas en la base: ", paste(faltan, collapse = ", "), call. = FALSE)

crudo$fecha <- as.Date(crudo$fecha)
n_arbol_crudo <- length(unique(tolower(trimws(crudo$id_arbol))))
n_grupos      <- length(unique(crudo$grupo))

ficha <- data.frame(
  Elemento = c("Pregunta", "Tipo de estudio", "Lugar", "Fechas de recolección", "Factor de interés",
               "Variable respuesta", "Unidad experimental", "Unidad de muestreo", "Tamaño de la muestra",
               "Instrumentos"),
  Descripción = c(
    "¿Cómo cambia el área de las hojas según el lugar donde crece el árbol?",
    "Observacional y comparativo: no se asignó sol o sombra, se eligieron árboles que ya estaban en cada condición",
    "Campus universitario, Cali (Valle del Cauca, Colombia)",
    paste(format(min(crudo$fecha), "%d/%m/%Y"), "a", format(max(crudo$fecha), "%d/%m/%Y")),
    "Exposición del árbol: sol (luz directa la mayor parte del día) o sombra",
    "Área de la hoja en cm²",
    "El árbol (la exposición es una propiedad suya)",
    "La hoja: 10 hojas adultas y sanas por árbol",
    paste0(n_arbol_crudo, " árboles; ", nrow(crudo), " hojas registradas (", n_grupos, " grupos)"),
    "Celular (cámara y GPS), papel blanco, lápiz y regla, y un script en R que calcula el área a partir de la foto"))
tabla(ficha, align = "ll")
Elemento Descripción
Pregunta ¿Cómo cambia el área de las hojas según el lugar donde crece el árbol?
Tipo de estudio Observacional y comparativo: no se asignó sol o sombra, se eligieron árboles que ya estaban en cada condición
Lugar Campus universitario, Cali (Valle del Cauca, Colombia)
Fechas de recolección 22/10/2026 a 29/10/2026
Factor de interés Exposición del árbol: sol (luz directa la mayor parte del día) o sombra
Variable respuesta Área de la hoja en cm²
Unidad experimental El árbol (la exposición es una propiedad suya)
Unidad de muestreo La hoja: 10 hojas adultas y sanas por árbol
Tamaño de la muestra 5 árboles; 50 hojas registradas (5 grupos)
Instrumentos Celular (cámara y GPS), papel blanco, lápiz y regla, y un script en R que calcula el área a partir de la foto

1.2 Sitio de estudio y protocolo

El estudio se hizo en el campus de la universidad, en Cali, en el valle geográfico del río Cauca (zona tropical). (Si quieres citar altitud, temperatura o régimen de lluvias, busca el dato en una fuente, por ejemplo la estación meteorológica más cercana.) Dentro del campus hay árboles en zonas abiertas y bajo otras copas, lo que da un gradiente natural de luz.

El protocolo, en resumen:

  1. Se eligieron árboles adultos y sanos, unos a pleno sol y otros a la sombra, y se registraron sus coordenadas con el GPS del celular.
  2. De cada árbol se tomaron 10 hojas adultas, expandidas y sin daño.
  3. Cada hoja se trazó sobre papel, se fotografió dentro de un marco rectangular de lados conocidos y se midió su área con el script de R (corrección de perspectiva con el marco como escala).
  4. Los datos se capturaron en una plantilla (una fila por hoja).

1.3 Mapa de los sitios

Cada punto es un árbol. Haz clic en un punto para ver sus datos.

# Un registro por árbol (con coordenadas normalizadas)
sitios <- unique(data.frame(grupo = crudo$grupo,
                            id_arbol = tolower(trimws(crudo$id_arbol)),
                            lat = a_numero(crudo$lat), lon = a_numero(crudo$lon),
                            exposicion = tolower(trimws(crudo$exposicion)),
                            stringsAsFactors = FALSE))
sitios$hojas <- as.integer(table(tolower(trimws(crudo$id_arbol)))[sitios$id_arbol])

buscar_foto <- function(id) {
  cand <- file.path(params$carpeta_fotos, paste0(id, c(".jpg", ".jpeg", ".png")))
  ex <- cand[file.exists(cand)]
  if (length(ex) > 0) ex[1] else NA_character_
}
sitios$foto <- vapply(sitios$id_arbol, buscar_foto, character(1))
cat("Árboles:", nrow(sitios), "| con foto:", sum(!is.na(sitios$foto)), "\n")
## Árboles: 5 | con foto: 0
sitios$popup <- vapply(seq_len(nrow(sitios)), function(i) {
  s <- sitios[i, ]
  img <- if (is.na(s$foto)) "" else paste0("<img src='", knitr::image_uri(s$foto), "' width='200'><br>")
  paste0(img, "<b>", s$id_arbol, "</b><br>Exposición: ", s$exposicion, "<br>Grupo ", s$grupo,
         "<br>Hojas registradas: ", s$hojas, "<br>", fmt(s$lat, 5), "; ", fmt(s$lon, 5))
}, character(1))
if (requireNamespace("leaflet", quietly = TRUE)) {
  library(leaflet)
  leaflet(sitios) %>%
    addProviderTiles(providers$Esri.WorldImagery, group = "Satélite") %>%
    addTiles(group = "Calles") %>%
    addCircleMarkers(lng = ~lon, lat = ~lat, radius = 9, weight = 2, color = "white",
                     fillColor = ~col_expo[exposicion], fillOpacity = 0.95,
                     label = ~id_arbol, popup = ~popup, popupOptions = popupOptions(maxWidth = 240)) %>%
    addLayersControl(baseGroups = c("Satélite", "Calles"), options = layersControlOptions(collapsed = FALSE)) %>%
    addLegend("bottomright", colors = unname(col_expo), labels = c("Sol", "Sombra"),
              title = "Exposición", opacity = 1) %>%
    addScaleBar("bottomleft")
} else {
  cat("El paquete 'leaflet' no está instalado: se muestra un mapa simple.\n")
  plot(sitios$lon, sitios$lat, pch = 21, cex = 2, bg = col_expo[sitios$exposicion],
       asp = 1 / cos(mean(sitios$lat) * pi / 180), xlab = "Longitud", ylab = "Latitud",
       main = "Ubicación de los árboles")
  text(sitios$lon, sitios$lat, sitios$id_arbol, cex = 0.7, pos = 3)
  legend("topright", legend = names(col_expo), pt.bg = col_expo, pch = 21, bty = "n")
}
if (any(!is.na(sitios$foto))) {
  cat("## Fotos de los árboles\n\n")
  for (i in which(!is.na(sitios$foto))) {
    cat(sprintf("**%s** (%s)\n\n![](%s){width=30%%}\n\n", sitios$id_arbol[i], sitios$exposicion[i], sitios$foto[i]))
  }
} else {
  cat("*(Opcional: guarda una foto de la copa de cada árbol en la carpeta `", params$carpeta_fotos,
      "/` con el ID como nombre, por ejemplo `sl1_la.jpg`, y aparecerá aquí.)*\n", sep = "")
}

(Opcional: guarda una foto de la copa de cada árbol en la carpeta fotos/ con el ID como nombre, por ejemplo sl1_la.jpg, y aparecerá aquí.)

2 Introducción

Pregunta. ¿Cómo cambia el área de las hojas según el lugar donde crece el árbol?

Por qué esperamos una diferencia. Una hoja equilibra dos cosas: captar luz y no perder demasiada agua ni calentarse de más. A pleno sol sobra luz y el riesgo es el exceso de calor y de transpiración, así que se esperan hojas más pequeñas. A la sombra la luz es el recurso escaso, y una lámina más grande intercepta más de ella.

Hipótesis. Las hojas de los árboles que crecen a la sombra tienen, en promedio, mayor área que las de los árboles que crecen a pleno sol. (Escribe tu hipótesis con tus palabras y justifícala antes de mirar los resultados.)

3 Métodos

Diseño. Se midieron 10 hojas por árbol en árboles ya clasificados como de sol (luz directa la mayor parte del día, sin cobertura de otros árboles ni edificios) o de sombra. Las hojas fueron adultas, expandidas y sin daño evidente.

Medición. Cada hoja se trazó sobre papel, se fotografió dentro de un marco rectangular de lados conocidos y un script calculó el área en cm². Los datos se anotaron en una plantilla: una fila por hoja, con grupo, árbol, coordenadas, número de hoja, área, exposición, fecha y observaciones.

Análisis (todo en R base, R version 4.6.1 (2026-06-24 ucrt)):

  1. Revisión de la base y decisiones documentadas sobre cada problema.
  2. Por árbol: n, media, mediana, desviación estándar y coeficiente de variación.
  3. Gráficas: un diagrama de cajas por árbol y otro por exposición.
  4. Valores atípicos: regla de Tukey dentro de cada árbol, más la revisión de las observaciones.
  5. Comparación sol–sombra con el árbol como unidad de análisis (se promedian las 10 hojas de cada árbol).
  6. ANOVA de un factor entre árboles (con Tukey) y ANOVA anidado (hojas dentro de árboles dentro de exposición).

4 Datos y revisión de calidad

Antes de calcular nada hay que mirar la base. Se trabaja sobre una copia (datos); la base original (crudo) no se toca.

4.1 Lo que trae la base

head(crudo, 6)
##   grupo id_arbol    lat      lon n_hoja area_cm2 exposicion      fecha
## 1     1   sl1_la 3.3458 -76.5337      1    37.42        sol 2026-10-22
## 2     1   sl1_la 3.3458 -76.5337      2    37.70        sol 2026-10-22
## 3     1   sl1_la 3.3458 -76.5337      3    37.34        sol 2026-10-22
## 4     1   sl1_la 3.3458 -76.5337      4    34.92        sol 2026-10-22
## 5     1   sl1_la 3.3458 -76.5337      5    32.49        sol 2026-10-22
## 6     1   sl1_la 3.3458 -76.5337      6    30.32        sol 2026-10-22
##   observaciones
## 1            NA
## 2            NA
## 3            NA
## 4            NA
## 5            NA
## 6            NA
sapply(crudo, function(x) class(x)[1])
##         grupo      id_arbol           lat           lon        n_hoja 
##     "numeric"   "character"     "numeric"     "numeric"     "numeric" 
##      area_cm2    exposicion         fecha observaciones 
##     "numeric"   "character"        "Date"     "logical"

La base tiene 50 filas y 5 valores distintos en la columna grupo. Si todo estuviera completo, serían (número de árboles × 10 hojas).

4.2 Revisiones

datos <- crudo
datos$fila <- seq_len(nrow(datos)) + 1L     # fila en la hoja de cálculo (la 1 es el encabezado)

# Normalizar texto
n_id_corregido   <- sum(datos$id_arbol != tolower(trimws(datos$id_arbol)))
n_expo_corregida <- sum(datos$exposicion != tolower(trimws(datos$exposicion)))
datos$id_arbol   <- tolower(trimws(datos$id_arbol))
datos$exposicion <- tolower(trimws(datos$exposicion))
stopifnot(all(datos$exposicion %in% c("sol", "sombra")))

# Números (por si alguien escribió coma decimal)
datos$lat <- a_numero(datos$lat); datos$lon <- a_numero(datos$lon)
datos$area_cm2 <- a_numero(datos$area_cm2)

# 1. Áreas faltantes o no válidas
malas <- is.na(datos$area_cm2) | datos$area_cm2 <= 0
# 2. Filas repetidas (mismo árbol y mismo número de hoja)
repetida <- duplicated(datos[, c("id_arbol", "n_hoja")])
# 3. Hojas por árbol
n_por_arbol <- table(datos$id_arbol[!repetida])
# 4. ¿Cada árbol tiene una sola exposición?
n_expo <- tapply(datos$exposicion, datos$id_arbol, function(x) length(unique(x)))
# 5. ¿Cuántos árboles por grupo?
arb_por_grupo <- tapply(datos$id_arbol, datos$grupo, function(x) length(unique(x)))
# 6. Valores extremos respecto a su propio árbol
mediana_arbol <- ave(datos$area_cm2, datos$id_arbol, FUN = function(x) median(x[!is.na(x) & x > 0]))
datos$razon_mediana <- datos$area_cm2 / mediana_arbol
extremos <- !is.na(datos$razon_mediana) & datos$area_cm2 > 0 &
            (datos$razon_mediana > 10 | datos$razon_mediana < 0.1)
# 7. Observaciones escritas
con_obs <- !is.na(datos$observaciones) & nzchar(trimws(as.character(datos$observaciones)))
# 8. Fechas por árbol
fechas_arbol <- tapply(format(datos$fecha, "%d/%m/%Y"), datos$id_arbol, function(x) paste(unique(x), collapse = ", "))

revision <- data.frame(
  Revisión = c("IDs con mayúsculas o espacios de más", "Exposiciones escritas de varias formas",
               "Áreas faltantes o <= 0", "Filas repetidas (árbol y n.º de hoja)",
               "Árboles con exposición distinta en sus filas", "Árboles que no tienen 10 hojas",
               "Hojas con un área > 10 veces o < 0,1 veces la mediana de su árbol",
               "Filas con observaciones escritas"),
  Resultado = c(n_id_corregido, n_expo_corregida, sum(malas), sum(repetida),
                sum(n_expo > 1), sum(n_por_arbol != 10), sum(extremos), sum(con_obs)))
tabla(revision, align = "lr")
Revisión Resultado
IDs con mayúsculas o espacios de más 0
Exposiciones escritas de varias formas 0
Áreas faltantes o <= 0 0
Filas repetidas (árbol y n.º de hoja) 0
Árboles con exposición distinta en sus filas 0
Árboles que no tienen 10 hojas 0
Hojas con un área > 10 veces o < 0,1 veces la mediana de su árbol 0
Filas con observaciones escritas 0
cat("Hojas por árbol:\n"); print(n_por_arbol)
## Hojas por árbol:
## 
##   sl1_ sl1_la sl2_bu sm1_gn sm2_na 
##     10     10     10     10     10
cat("\nÁrboles por grupo:\n"); print(arb_por_grupo)
## 
## Árboles por grupo:
## 1 2 3 4 5 
## 1 1 1 1 1
cat("\nFecha(s) de medición por árbol:\n"); print(fechas_arbol)
## 
## Fecha(s) de medición por árbol:
##         sl1_       sl1_la       sl2_bu       sm1_gn       sm2_na 
## "29/10/2026" "22/10/2026" "23/10/2026" "22/10/2026" "29/10/2026"
cat("\nRango de n_hoja:", range(datos$n_hoja), "\n")
## 
## Rango de n_hoja: 1 10
cat("\nÁrboles por exposición:\n"); print(table(unique(datos[, c("id_arbol", "exposicion")])$exposicion))
## 
## Árboles por exposición:
## 
##    sol sombra 
##      3      2

Formato de los IDs. Los IDs de esta base parecen un código (por ejemplo sl1_la: sol, árbol 1, y las letras finales podrían ser la especie). Comprueba que todos sigan el mismo formato: un ID que no lo cumple puede ser un error de captura o un dato incompleto.

ids <- sort(unique(datos$id_arbol))
formato <- ifelse(nchar(ids) == max(nchar(ids)), "igual que la mayoría", "DISTINTO: revisar")
tabla(data.frame(ID = ids, Formato = formato), align = "ll")
ID Formato
sl1_ DISTINTO: revisar
sl1_la igual que la mayoría
sl2_bu igual que la mayoría
sm1_gn igual que la mayoría
sm2_na igual que la mayoría

Ubicación. Las coordenadas deberían caer en la zona del campus. Se calcula la distancia entre cada par de árboles (fórmula de Haversine):

arb <- aggregate(cbind(lat, lon) ~ id_arbol + grupo + exposicion, data = datos, FUN = mean)
pares <- t(combn(nrow(arb), 2))
d <- distancia_m(arb$lat[pares[, 1]], arb$lon[pares[, 1]], arb$lat[pares[, 2]], arb$lon[pares[, 2]])
dist_tab <- data.frame(arbol_a = arb$id_arbol[pares[, 1]], arbol_b = arb$id_arbol[pares[, 2]],
                       distancia_m = round(d))
dist_tab <- dist_tab[order(dist_tab$distancia_m), ]
tabla(dist_tab, row.names = FALSE, col.names = c("Árbol A", "Árbol B", "Distancia (m)"))
Árbol A Árbol B Distancia (m)
sl2_bu sm1_gn 22
sl1_la sl2_bu 109
sm1_gn sm2_na 114
sl1_la sm1_gn 130
sl2_bu sm2_na 134
sl1_la sm2_na 244
sl1_ sm2_na 3641
sl1_ sm1_gn 3744
sl2_bu sl1_ 3766
sl1_la sl1_ 3869
# Árboles de grupos distintos a menos de dist_duplicado_m: posible mismo árbol medido dos veces
otro_grupo <- arb$grupo[pares[, 1]] != arb$grupo[pares[, 2]]
repetidos <- dist_tab[d < params$dist_duplicado_m & otro_grupo, ]

# Árbol alejado del resto: distancia a la mediana de las coordenadas
dist_centro <- distancia_m(median(arb$lat), median(arb$lon), arb$lat, arb$lon)
lejanos <- arb$id_arbol[dist_centro > 1000]

Ningún par de árboles de grupos distintos está a menos de 5 m: no hay indicios de árboles medidos dos veces. Atención: sl1_ queda a más de 1 km del resto de árboles (3744 m). Si todos los árboles deben estar en el campus, revisa que sus coordenadas estén bien escritas (por ejemplo, un dígito mal copiado en la latitud o la longitud).

4.3 Decisiones

Cada problema se resuelve con una regla explícita, y las filas originales se conservan en la base (usar y motivo) para poder auditar lo que se hizo.

datos$area_cm2_original <- datos$area_cm2
datos$usar <- TRUE
datos$motivo <- ""
datos$corregida <- FALSE

marcar <- function(filas, motivo) {
  filas <- filas & datos$usar
  datos$usar[filas] <<- FALSE
  datos$motivo[filas] <<- motivo
  sum(filas)
}
n_repetidas <- marcar(repetida, "fila repetida")
n_malas     <- marcar(malas, "sin área o área <= 0")

# Error de unidad (mm² en lugar de cm²): se corrige dividiendo por 100
mm2 <- datos$usar & !is.na(datos$razon_mediana) & datos$razon_mediana > 10
datos$area_cm2[mm2] <- datos$area_cm2[mm2] / 100
datos$corregida[mm2] <- TRUE
n_mm2 <- sum(mm2)

limpios <- datos[datos$usar, ]
limpios$expo <- factor(limpios$exposicion, levels = c("sol", "sombra"))
orden_arbol <- unique(limpios[order(limpios$grupo, limpios$id_arbol), "id_arbol"])
limpios$id_arbol <- factor(limpios$id_arbol, levels = orden_arbol)
limpios <- limpios[order(limpios$id_arbol, limpios$n_hoja), ]

decisiones <- data.frame(
  Problema = c("Fila repetida", "Área faltante o <= 0", "Área en mm² en lugar de cm²"),
  Filas = c(n_repetidas, n_malas, n_mm2),
  Decisión = c("Eliminar la copia", "Excluir la hoja (no hay dato válido)", "Dividir por 100"))
tabla(decisiones, align = "lrl")
Problema Filas Decisión
Fila repetida 0 Eliminar la copia
Área faltante o <= 0 0 Excluir la hoja (no hay dato válido)
Área en mm² en lugar de cm² 0 Dividir por 100
cat("Filas originales:", nrow(crudo), "\n",
    "Filas que se usan:", nrow(limpios), "\n",
    "Árboles:", length(unique(limpios$id_arbol)), "\n")
## Filas originales: 50 
##  Filas que se usan: 50 
##  Árboles: 5
if (params$guardar_limpios) {
  write.csv(limpios[, c("grupo", "id_arbol", "lat", "lon", "n_hoja", "area_cm2", "exposicion",
                        "fecha", "observaciones", "corregida")],
            "datos_limpios_area_foliar.csv", row.names = FALSE, na = "")
}

Idea clave. Ninguna decisión es “correcta” por sí sola; lo que importa es que sean explícitas y reproducibles.

5 Resultados

5.1 Estadística descriptiva por árbol

resumen <- do.call(rbind, lapply(split(limpios, limpios$id_arbol), function(d) {
  data.frame(arbol = as.character(d$id_arbol[1]), exposicion = d$exposicion[1], n = nrow(d),
             media = mean(d$area_cm2), mediana = median(d$area_cm2), de = sd(d$area_cm2),
             cv_pct = cv(d$area_cm2), minimo = min(d$area_cm2), maximo = max(d$area_cm2))
}))
tabla(resumen, row.names = FALSE, digits = c(0, 0, 0, 1, 1, 1, 1, 1, 1),
      col.names = c("Árbol", "Exposición", "n", "Media", "Mediana", "DE", "CV (%)", "Mín.", "Máx."),
      caption = "Área foliar (cm²) por árbol. DE = desviación estándar; CV = DE / media.")
Área foliar (cm²) por árbol. DE = desviación estándar; CV = DE / media.
Árbol Exposición n Media Mediana DE CV (%) Mín. Máx.
sl1_la sol 10 32.7 33.1 4.5 13.8 23.6 37.7
sm1_gn sombra 10 67.1 66.7 12.5 18.7 49.0 85.0
sl2_bu sol 10 41.9 40.9 4.0 9.5 36.7 50.1
sm2_na sombra 10 15.9 16.4 1.9 11.7 13.4 19.0
sl1_ sol 10 14.7 14.6 1.4 9.8 12.7 16.3

Las medias de los árboles van de 14,7 a 67,1 cm², y el CV (variación de las hojas de un árbol respecto a su propia media) va de 10 % a 19 %. Compara la diferencia entre árboles con la variación dentro de cada árbol.

5.2 Un diagrama de cajas por árbol

par(mar = c(4.5, 4.8, 3, 1))
col_arb <- col_expo[resumen$exposicion]
boxplot(area_cm2 ~ id_arbol, data = limpios, outline = FALSE, las = 1,
        col = adjustcolor(col_arb, 0.55), border = "grey25",
        xlab = "Árbol", ylab = expression("Área foliar (cm"^2*")"),
        main = "Área foliar por árbol")
stripchart(area_cm2 ~ id_arbol, data = limpios, vertical = TRUE, method = "jitter", jitter = 0.12,
           pch = 16, col = adjustcolor("black", 0.45), add = TRUE)
legend("topright", legend = c("sol", "sombra"), fill = adjustcolor(col_expo, 0.55), bty = "n")
Área foliar de cada hoja, por árbol. Color = exposición.

Área foliar de cada hoja, por árbol. Color = exposición.

Cada caja resume las 10 hojas de un árbol (la línea central es la mediana y la caja contiene la mitad central de las hojas); los puntos son las hojas.

5.3 Valores atípicos: ¿error o hoja realmente distinta?

Criterio. Se revisa cada hoja con dos preguntas, en este orden:

  1. ¿Hay una causa de medición (unidad equivocada, hoja dañada, trazo dudoso, foto borrosa)? Si sí, se corrige o se excluye.
  2. Si no hay causa de medición pero la hoja queda fuera de la regla de Tukey de su árbol (por debajo de Q1 − 1,5·RIC o por encima de Q3 + 1,5·RIC), es una hoja realmente distinta: se conserva y se comprueba cuánto cambia el resultado sin ella (análisis de sensibilidad).
fuera <- do.call(rbind, lapply(split(limpios, limpios$id_arbol), function(d) {
  d[d$area_cm2 %in% boxplot.stats(d$area_cm2)$out, c("id_arbol", "n_hoja", "area_cm2", "observaciones")]
}))
if (is.null(fuera) || nrow(fuera) == 0) {
  cat("Ninguna hoja queda fuera de los límites de Tukey de su árbol.\n")
} else {
  tabla(fuera, row.names = FALSE, caption = "Hojas fuera de los límites de Tukey de su árbol.")
}
Hojas fuera de los límites de Tukey de su árbol.
id_arbol n_hoja area_cm2 observaciones
sl2_bu 2 50.14 NA
sens <- do.call(rbind, lapply(split(limpios, limpios$id_arbol), function(d) {
  out <- boxplot.stats(d$area_cm2)$out
  data.frame(arbol = as.character(d$id_arbol[1]), exposicion = d$exposicion[1],
             media = mean(d$area_cm2), mediana = median(d$area_cm2),
             media_sin_atipicas = mean(d$area_cm2[!d$area_cm2 %in% out]))
}))
dif_sens <- sapply(c("media", "mediana", "media_sin_atipicas"), function(v)
  mean(sens[[v]][sens$exposicion == "sombra"]) - mean(sens[[v]][sens$exposicion == "sol"]))
tabla(data.frame(Resumen = c("Media (todas las hojas)", "Mediana", "Media sin las hojas atípicas"),
                 Dif = unname(dif_sens)), digits = 1,
      col.names = c("Cómo se resume cada árbol", "Sombra − sol (cm²)"),
      caption = "Sensibilidad de la diferencia entre exposiciones a las hojas atípicas.")
Sensibilidad de la diferencia entre exposiciones a las hojas atípicas.
Cómo se resume cada árbol Sombra − sol (cm²)
Media (todas las hojas) 11.7
Mediana 12.0
Media sin las hojas atípicas 12.0

La diferencia entre sombra y sol va de 11,7 a 12,0 cm² según cómo se trate la hoja atípica (si la hay). Cuando una conclusión depende de una sola hoja, hay que decirlo.

5.4 Un diagrama de cajas por exposición

simbolos <- c(16, 17, 15, 18, 8, 3, 4, 1)
simbolo <- simbolos[as.integer(limpios$id_arbol)]
par(mar = c(4.5, 4.8, 3, 1))
boxplot(area_cm2 ~ expo, data = limpios, outline = FALSE, las = 1,
        col = adjustcolor(col_expo, 0.5), border = "grey25",
        xlab = "Exposición", ylab = expression("Área foliar (cm"^2*")"), main = "Hojas por exposición")
set.seed(2)
points(as.integer(limpios$expo) + runif(nrow(limpios), -0.15, 0.15), limpios$area_cm2,
       pch = simbolo, col = adjustcolor("black", 0.6))
legend("topleft", legend = levels(limpios$id_arbol), pch = simbolos[seq_along(levels(limpios$id_arbol))],
       bty = "n", title = "Árbol", cex = 0.85)
Hojas de sol y de sombra. Cada símbolo es un árbol distinto.

Hojas de sol y de sombra. Cada símbolo es un árbol distinto.

Este gráfico es descriptivo. Fíjate en que los símbolos iguales (hojas del mismo árbol) tienden a quedar juntos: por eso las hojas no son independientes y no se pueden usar como si fueran 50 datos independientes en una prueba.

5.5 Unidad de análisis: el árbol

Lo que se asignó a sol o a sombra es el árbol, no la hoja. Las 10 hojas de un mismo árbol comparten suelo, edad, genética y luz. Por eso se promedian y se compara un valor por árbol.

medias <- aggregate(area_cm2 ~ id_arbol + exposicion, data = limpios, FUN = mean)
medias$exposicion <- factor(medias$exposicion, levels = c("sombra", "sol"))
medias <- medias[order(medias$exposicion, medias$id_arbol), ]
tabla(medias, row.names = FALSE, digits = 1, col.names = c("Árbol", "Exposición", "Media del árbol (cm²)"))
Árbol Exposición Media del árbol (cm²)
sm1_gn sombra 67.1
sm2_na sombra 15.9
sl1_la sol 32.7
sl2_bu sol 41.9
sl1_ sol 14.7
n_som <- sum(medias$exposicion == "sombra"); n_sol <- sum(medias$exposicion == "sol")
media_som <- mean(medias$area_cm2[medias$exposicion == "sombra"])
media_sol <- mean(medias$area_cm2[medias$exposicion == "sol"])
dif_g <- media_som - media_sol

Hay 2 árbol(es) de sombra y 3 de sol: 5 datos independientes, no 50. La media de los árboles de sombra es 41,5 cm² y la de sol 29,8 cm²; la diferencia observada (sombra − sol) es de 11,7 cm².

5.5.1 ¿Qué tan raro sería ver esa diferencia por azar?

Con tan pocos árboles se puede hacer una prueba de permutación exacta: si la exposición no importara, cualquier forma de repartir las etiquetas “sol” y “sombra” entre los árboles sería igual de probable. Se calcula la diferencia para todos los repartos posibles y se mira dónde queda el observado.

y <- medias$area_cm2
es_sombra <- medias$exposicion == "sombra"
repartos <- combn(length(y), n_som)
dif_perm <- apply(repartos, 2, function(i) mean(y[i]) - mean(y[-i]))
dif_obs <- mean(y[es_sombra]) - mean(y[!es_sombra])
p_perm <- mean(abs(dif_perm) >= abs(dif_obs) - 1e-9)
p_min  <- 1 / ncol(repartos)

par(mar = c(4.5, 4.8, 3, 1))
plot(dif_perm, rep(0, length(dif_perm)), pch = 21, bg = "grey85", cex = 2.2, yaxt = "n",
     ylab = "", xlab = "Diferencia sombra − sol (cm²)", main = paste(ncol(repartos), "repartos posibles"))
points(dif_obs, 0, pch = 21, bg = col_expo["sombra"], cex = 2.6)
abline(v = 0, lty = 3)
legend("topright", legend = "reparto observado", pch = 21, pt.bg = col_expo["sombra"], bty = "n", pt.cex = 1.6)
Diferencia sombra − sol para cada reparto posible de las etiquetas entre los árboles.

Diferencia sombra − sol para cada reparto posible de las etiquetas entre los árboles.

Hay 10 repartos posibles y p = 0,600. Con 2 y 3 árboles, el valor de p más pequeño que cualquier resultado podría dar es 0,100: el diseño no puede dar resultados “significativos” al 5 %, sean cuales sean los datos.

5.5.2 Lo que habría pasado ignorando el árbol

t_hojas <- t.test(area_cm2 ~ expo, data = limpios)          # incorrecto: hojas como independientes
t_arbol <- t.test(area_cm2 ~ exposicion, data = medias)     # promedios por árbol (Welch)
comparacion <- data.frame(
  Análisis = c("Hojas como datos independientes (incorrecto)", "Un promedio por árbol (Welch)",
               "Un promedio por árbol (permutación exacta)"),
  n_sol = c(sum(limpios$expo == "sol"), n_sol, n_sol),
  n_sombra = c(sum(limpios$expo == "sombra"), n_som, n_som),
  p = c(fmt_p(t_hojas$p.value), fmt_p(t_arbol$p.value), fmt_p(p_perm)))
tabla(comparacion, col.names = c("Análisis", "n sol", "n sombra", "p"), align = "lrrr")
Análisis n sol n sombra p
Hojas como datos independientes (incorrecto) 30 20 0,086
Un promedio por árbol (Welch) 3 2 0,727
Un promedio por árbol (permutación exacta) 3 2 0,600

Tratar las hojas como independientes multiplica por 10 el tamaño de muestra y cambia el p de 0,600 (permutación) a 0,086. Esa precisión es falsa: se llama pseudorreplicación. Las hojas de un árbol son 10 mediciones de lo mismo, no 10 árboles.

6 Diseño experimental, ANOVA y comparaciones post-ANOVA

El análisis de varianza (ANOVA) permite usar la estructura del diseño. Aquí se responden dos preguntas:

Pregunta Herramienta Después del ANOVA
¿Difieren entre sí los árboles medidos? ANOVA de un factor (árbol) Tukey HSD: ¿cuáles árboles?
¿Hay efecto de la exposición respetando que las hojas están dentro de árboles? ANOVA anidado (árbol dentro de exposición) Componentes de varianza

6.1 El diseño

  • Factor de interés (fijo): exposición, con 2 niveles (sol, sombra).
  • Unidad experimental: el árbol, porque la exposición es una propiedad del árbol.
  • Unidad de muestreo (submuestra): la hoja. Las 10 hojas de un árbol miden al árbol; no son réplicas de la exposición.

Modelo: log(área) ~ exposición + árbol(exposición) + error de hoja. El término árbol(exposición) es el error experimental contra el que se prueba la exposición.

¿Por qué el logaritmo del área? El área es positiva y las hojas grandes varían más que las pequeñas. Con el logaritmo, la variabilidad se parece entre árboles y los efectos se interpretan como razones (“1,3 veces”).

por_arbol     <- aggregate(area_cm2 ~ id_arbol, data = limpios, FUN = function(x) c(media = mean(x), de = sd(x)))
por_arbol_log <- aggregate(log(area_cm2) ~ id_arbol, data = limpios, FUN = function(x) c(media = mean(x), de = sd(x)))
r_orig <- cor(por_arbol$area_cm2[, "media"], por_arbol$area_cm2[, "de"])
r_log  <- cor(por_arbol_log[[2]][, "media"], por_arbol_log[[2]][, "de"])
c(escala_original = r_orig, escala_log = r_log)
## escala_original      escala_log 
##       0.9513261       0.6518606

La correlación entre la media de cada árbol y su desviación estándar es 0,95 en la escala original y 0,65 con el logaritmo. (Con solo 5 árboles esta comparación es muy aproximada.)

6.2 ANOVA de un factor: ¿difieren los árboles?

Aquí las hojas sí son réplicas (de cada árbol) y la pregunta es por esos árboles concretos, no por la exposición.

m1 <- lm(log(area_cm2) ~ id_arbol, data = limpios)
a1 <- tabla_anova(m1)
a1$Fuente <- c("Entre árboles", "Dentro de árboles (hojas)")
tabla(formato_anova(a1), row.names = FALSE, align = "lrrrrr",
      caption = "ANOVA de un factor sobre log(área).")
ANOVA de un factor sobre log(área).
Fuente gl SC CM F p
Entre árboles 4 16,575 4,144 228,79 < 0,001
Dentro de árboles (hojas) 45 0,815 0,018

Hay diferencias entre árboles: F(4; 45) = 228,8, p < 0,001. El ANOVA solo dice que al menos un árbol difiere; para saber cuáles hay que hacer comparaciones múltiples.

Supuestos. Los residuos deberían ser aproximadamente normales y tener varianza parecida entre árboles.

par(mfrow = c(1, 2), mar = c(4.5, 4.8, 3, 1))
plot(fitted(m1), resid(m1), pch = 16, col = adjustcolor("black", 0.5), las = 1,
     xlab = "Valor ajustado (log área)", ylab = "Residuo", main = "Residuos vs ajustados")
abline(h = 0, lty = 2)
qqnorm(resid(m1), pch = 16, col = adjustcolor("black", 0.5), las = 1, main = "Normalidad de los residuos",
       xlab = "Cuantiles teóricos", ylab = "Cuantiles de los residuos")
qqline(resid(m1))
Diagnóstico del ANOVA de un factor: residuos frente a valores ajustados (izquierda) y gráfico cuantil-cuantil (derecha).

Diagnóstico del ANOVA de un factor: residuos frente a valores ajustados (izquierda) y gráfico cuantil-cuantil (derecha).

par(mfrow = c(1, 1))

shap1 <- shapiro.test(resid(m1))
flig1 <- fligner.test(log(area_cm2) ~ id_arbol, data = limpios)
kw1   <- kruskal.test(log(area_cm2) ~ id_arbol, data = limpios)
c(shapiro_p = shap1$p.value, fligner_p = flig1$p.value, kruskal_p = kw1$p.value)
##    shapiro_p    fligner_p    kruskal_p 
## 4.119529e-01 1.888595e-01 4.743825e-09

Shapiro-Wilk (normalidad): p = 0,412. Fligner-Killeen (igualdad de varianzas): p = 0,189. Kruskal-Wallis (alternativa sin supuesto de normalidad): p = < 0,001. Un p grande significa que no hay evidencia contra el supuesto; un p pequeño, que sí la hay. Mira siempre el gráfico: los puntos del cuantil-cuantil deberían seguir la recta y los residuos no deberían abrirse como un embudo.

6.3 Post-ANOVA: ¿qué árboles difieren? (Tukey HSD)

Con 5 árboles hay 10 comparaciones por pares. Hacer pruebas t sueltas infla la probabilidad de algún falso positivo; Tukey HSD compara todos los pares y controla ese error para el conjunto.

tk <- TukeyHSD(aov(log(area_cm2) ~ id_arbol, data = limpios))$id_arbol
tk_tab <- data.frame(comparacion = rownames(tk), razon = exp(tk[, "diff"]),
                     li = exp(tk[, "lwr"]), ls = exp(tk[, "upr"]), p_aj = tk[, "p adj"],
                     stringsAsFactors = FALSE)
tk_tab <- tk_tab[order(tk_tab$p_aj), ]

# ¿Los árboles del par tienen la misma exposición?
expo_de <- setNames(resumen$exposicion, resumen$arbol)
# los IDs tienen guion bajo pero no guion, así que se separa por "-"
partes  <- strsplit(tk_tab$comparacion, "-")
misma_expo <- sapply(partes, function(x) expo_de[x[1]] == expo_de[x[2]])
n_misma_sig <- sum(misma_expo & tk_tab$p_aj < 0.05)

tabla(data.frame(Comparación = gsub("-", " / ", tk_tab$comparacion),
                 Razón = fmt(tk_tab$razon, 2),
                 IC = paste0("[", fmt(tk_tab$li, 2), "; ", fmt(tk_tab$ls, 2), "]"),
                 p = fmt_p(tk_tab$p_aj),
                 Dif = ifelse(tk_tab$p_aj < 0.05, "sí", "no"),
                 Expo = ifelse(misma_expo, "misma", "distinta")),
      col.names = c("Comparación (A / B)", "Razón A / B", "IC 95 % (Tukey)", "p ajustado",
                    "Difieren (5 %)", "Exposición del par"),
      align = "lrrrrr",
      caption = "Comparaciones de Tukey entre árboles (escala logarítmica, mostradas como razones).")
Comparaciones de Tukey entre árboles (escala logarítmica, mostradas como razones).
Comparación (A / B) Razón A / B IC 95 % (Tukey) p ajustado Difieren (5 %) Exposición del par
sm2_na / sm1_gn 0,24 [0,20; 0,28] < 0,001 sí misma
sl1_ / sm1_gn 0,22 [0,19; 0,26] < 0,001 sí distinta
sl1_ / sl2_bu 0,35 [0,30; 0,42] < 0,001 sí misma
sm2_na / sl2_bu 0,38 [0,32; 0,45] < 0,001 sí distinta
sl1_ / sl1_la 0,45 [0,38; 0,53] < 0,001 sí misma
sm2_na / sl1_la 0,49 [0,41; 0,58] < 0,001 sí distinta
sm1_gn / sl1_la 2,04 [1,72; 2,42] < 0,001 sí distinta
sl2_bu / sm1_gn 0,63 [0,53; 0,75] < 0,001 sí distinta
sl2_bu / sl1_la 1,29 [1,08; 1,53] 0,001 sí misma
sl1_ / sm2_na 0,92 [0,78; 1,10] 0,677 no distinta
k <- nrow(tk_tab)
par(mar = c(4.5, 10, 3, 1))
plot(tk_tab$razon, seq_len(k), log = "x", pch = 16, yaxt = "n", ylab = "",
     xlim = range(c(tk_tab$li, tk_tab$ls)), cex = 1.2,
     col = ifelse(tk_tab$p_aj < 0.05, "#D55E00", "grey40"),
     xlab = "Razón entre árboles (escala logarítmica)", main = "Comparaciones por pares")
segments(tk_tab$li, seq_len(k), tk_tab$ls, seq_len(k), lwd = 2,
         col = ifelse(tk_tab$p_aj < 0.05, "#D55E00", "grey40"))
axis(2, at = seq_len(k), labels = gsub("-", " / ", tk_tab$comparacion), las = 1, cex.axis = 0.8)
abline(v = 1, lty = 2)
Razón entre árboles con su intervalo de Tukey. Si el intervalo cruza 1, no hay diferencia detectable.

Razón entre árboles con su intervalo de Tukey. Si el intervalo cruza 1, no hay diferencia detectable.

En 9 de las 10 comparaciones los árboles difieren de forma detectable. De esas, 4 son entre árboles de la misma exposición: dos árboles de sol (o dos de sombra) pueden diferir tanto como uno de sol y uno de sombra. Esa variación entre árboles es el “ruido” contra el que tiene que competir el efecto de la exposición.

6.4 ANOVA anidado: el efecto de la exposición con el error correcto

Las hojas están dentro de árboles y los árboles dentro de una exposición. El efecto de la exposición se prueba contra la variación entre árboles, no contra la variación entre hojas.

m_anid <- lm(log(area_cm2) ~ expo + id_arbol, data = limpios)   # cada árbol tiene una sola exposición
a_an <- tabla_anova(m_anid)
# Fila 1 = exposición, fila 2 = árbol (dentro de exposición), fila 3 = hojas
F_expo  <- a_an$CM[1] / a_an$CM[2]; p_expo  <- pf(F_expo,  a_an$gl[1], a_an$gl[2], lower.tail = FALSE)
F_arbol <- a_an$CM[2] / a_an$CM[3]; p_arbol <- pf(F_arbol, a_an$gl[2], a_an$gl[3], lower.tail = FALSE)
p_ingenuo <- a_an$p[1]

tab_an <- data.frame(
  Fuente = c("Exposición", "Árbol (dentro de exposición)", "Hojas (residual)"),
  gl = a_an$gl, SC = fmt(a_an$SC, 3), CM = fmt(a_an$CM, 3),
  Contra = c("CM de árbol", "CM de hojas", ""),
  F = c(fmt(F_expo, 2), fmt(F_arbol, 2), ""),
  p = c(fmt_p(p_expo), fmt_p(p_arbol), ""))
tabla(tab_an, row.names = FALSE, align = "lrrrlrr",
      caption = "ANOVA anidado sobre log(área): hojas dentro de árboles dentro de exposición.")
ANOVA anidado sobre log(área): hojas dentro de árboles dentro de exposición.
Fuente gl SC CM Contra F p
Exposición 1 0,381 0,381 CM de árbol 0,07 0,808
Árbol (dentro de exposición) 3 16,193 5,398 CM de hojas 298,03 < 0,001
Hojas (residual) 45 0,815 0,018
  • Exposición: F(1; 3) = 0,07, p = 0,808. Con el árbol como unidad no hay evidencia suficiente de diferencia entre sol y sombra.
  • Árbol dentro de exposición: p = < 0,001. Árboles de la misma exposición difieren entre sí.
  • La versión ingenua (probar la exposición contra el CM de las hojas) daría p = < 0,001, una certeza que los datos no respaldan porque solo hay 5 árboles.

Cuánto de la variación se debe al árbol y cuánto a las hojas (componentes de varianza, aproximados):

n0 <- nrow(limpios) / nlevels(limpios$id_arbol)
s2_res <- a_an$CM[3]
s2_arb <- max((a_an$CM[2] - a_an$CM[3]) / n0, 0)
pct_arb <- 100 * s2_arb / (s2_arb + s2_res)
c(var_arbol = s2_arb, var_hoja = s2_res, pct_arbol = pct_arb)
##   var_arbol    var_hoja   pct_arbol 
##  0.53796435  0.01811122 96.74302924
r2_arb <- summary(m1)$r.squared

Alrededor del 97 % de la variación (en log) que queda después de quitar la exposición se debe a diferencias entre árboles, y el árbol por sí solo explica el 95 % de la variación del logaritmo del área. Por eso conviene medir más árboles antes que más hojas por árbol.

6.5 ¿Se puede hacer un ANOVA de bloques por grupo?

En el diseño de referencia, cada grupo mide árboles de sol y de sombra, y el grupo sirve de bloque. En esta base cada grupo tiene un solo árbol (ver “Árboles por grupo” en la revisión), así que el grupo y el árbol son lo mismo y no se pueden separar: el ANOVA de bloques con interacción grupo × exposición no tiene grados de libertad para estimarse. Por eso aquí no se hace. Para poder hacerlo, cada grupo tendría que medir al menos 2 árboles de sol y 2 de sombra.

resumen_a <- data.frame(
  Pregunta = c("¿Difieren los árboles?", "¿Qué árboles difieren?",
               "Sol vs sombra con árbol como unidad (Welch)",
               "Sol vs sombra con árbol como unidad (permutación exacta)",
               "Sol vs sombra, ANOVA anidado"),
  Herramienta = c("ANOVA de un factor", "Tukey HSD", "Prueba t sobre promedios", "Permutación", "F anidada"),
  Resultado = c(paste0("F = ", fmt(a1$F[1], 1), ", p ", ifelse(a1$p[1] < 0.001, "< 0,001", paste("=", fmt_p(a1$p[1])))),
                paste(sum(tk_tab$p_aj < 0.05), "de", nrow(tk_tab), "pares"),
                paste0("p = ", fmt_p(t_arbol$p.value)),
                paste0("p = ", fmt_p(p_perm)),
                paste0("F = ", fmt(F_expo, 2), ", p = ", fmt_p(p_expo))))
tabla(resumen_a, align = "lll", caption = "Resumen de los análisis.")
Resumen de los análisis.
Pregunta Herramienta Resultado
¿Difieren los árboles? ANOVA de un factor F = 228,8, p < 0,001
¿Qué árboles difieren? Tukey HSD 9 de 10 pares
Sol vs sombra con árbol como unidad (Welch) Prueba t sobre promedios p = 0,727
Sol vs sombra con árbol como unidad (permutación exacta) Permutación p = 0,600
Sol vs sombra, ANOVA anidado F anidada F = 0,07, p = 0,808

7 Qué muestra el mapa con el área media

mapa_res <- merge(medias, sitios[, c("id_arbol", "lat", "lon", "foto")], by = "id_arbol")
mapa_res$popup <- vapply(seq_len(nrow(mapa_res)), function(i) {
  s <- mapa_res[i, ]
  img <- if (is.na(s$foto)) "" else paste0("<img src='", knitr::image_uri(s$foto), "' width='200'><br>")
  paste0(img, "<b>", s$id_arbol, "</b> (", s$exposicion, ")<br>Área media: ", fmt(s$area_cm2), " cm²")
}, character(1))

if (requireNamespace("leaflet", quietly = TRUE)) {
  library(leaflet)
  leaflet(mapa_res) %>%
    addProviderTiles(providers$Esri.WorldImagery, group = "Satélite") %>%
    addTiles(group = "Calles") %>%
    addCircleMarkers(lng = ~lon, lat = ~lat, radius = ~sqrt(area_cm2) * 1.8, weight = 1.5, color = "white",
                     fillColor = ~col_expo[as.character(exposicion)], fillOpacity = 0.85,
                     label = ~paste0(id_arbol, ": ", round(area_cm2), " cm²"),
                     popup = ~popup, popupOptions = popupOptions(maxWidth = 240)) %>%
    addLayersControl(baseGroups = c("Satélite", "Calles"), options = layersControlOptions(collapsed = FALSE)) %>%
    addLegend("bottomright", colors = unname(col_expo), labels = c("Sol", "Sombra"),
              title = "Exposición (tamaño = área media)", opacity = 1) %>%
    addScaleBar("bottomleft")
} else {
  plot(mapa_res$lon, mapa_res$lat, pch = 21, bg = col_expo[as.character(mapa_res$exposicion)],
       cex = sqrt(mapa_res$area_cm2) / 3, asp = 1 / cos(mean(mapa_res$lat) * pi / 180),
       xlab = "Longitud", ylab = "Latitud", main = "Área foliar media por árbol")
}

El tamaño del círculo es proporcional al área media de cada árbol. Sirve para mirar si hay un patrón espacial (por ejemplo, una zona del campus con hojas grandes) que pudiera confundirse con el efecto de la exposición.

8 Discusión

Qué encontramos. Con el árbol como unidad de análisis, los árboles de sombra tuvieron hojas con un área media mayor que los de sol: 41,5 cm² frente a 29,8 cm² (media de los promedios por árbol; 2 árboles de sombra y 3 de sol), una diferencia de 11,7 cm². El sentido de la diferencia coincide con la hipótesis inicial.

Qué tan fuerte es la evidencia. La prueba de permutación da p = 0,600 y el ANOVA anidado p = 0,808. No hay evidencia suficiente para afirmar que la exposición cambia el área foliar: con tan pocos árboles, esta prueba tiene muy poca potencia. Además, los intervalos y los rangos se solapan: al menos un árbol de sombra tiene un promedio menor que algún árbol de sol, es decir, la separación entre las dos exposiciones no es limpia.

Lo que domina es la variación entre árboles. El árbol explica cerca del 95 % de la variación del logaritmo del área, y Tukey encontró 9 pares de árboles distintos, 4 de ellos con la misma exposición. Es decir, las diferencias entre árboles son grandes aunque tengan la misma exposición.

Posibles explicaciones ambientales (hipótesis, no algo que estos datos demuestren):

  • Luz. A la sombra, una lámina mayor intercepta más luz; las hojas de sol, más pequeñas y gruesas, ya tienen luz de sobra.
  • Temperatura. Una hoja pequeña tiene una capa de aire más delgada en su borde y se enfría con más facilidad, ventaja a pleno sol.
  • Agua. A pleno sol la demanda de evaporación es mayor; hojas más pequeñas pierden menos agua.

Qué más pudo influir (explicaciones alternativas que debes discutir):

  • La especie. Los IDs sugieren que los árboles pueden ser de especies distintas. Si los árboles de sombra son por casualidad de especies de hoja grande (o pequeña), la diferencia sería de composición de especies y no de respuesta a la luz. Si registraste la especie de cada árbol, inclúyela aquí.
  • Fechas distintas. Los árboles se midieron en fechas distintas (ver la revisión de fechas): sl1_ = 29/10/2026; sl1_la = 22/10/2026; sl2_bu = 23/10/2026; sm1_gn = 22/10/2026; sm2_na = 29/10/2026. El estado hídrico del árbol (por ejemplo, antes o después de lluvia) puede cambiar el tamaño de la hoja.
  • Edad y tamaño del árbol y la calidad del suelo donde crece.
  • Cómo se eligieron las hojas. Escoger “las más bonitas” o hojas de una sola parte de la copa sesga el área.
  • La clasificación en sol y sombra se hizo a ojo, sin medir la luz.

Resultados frente a hipótesis.

Es un resultado (lo que los datos muestran) Es una hipótesis (lo que habría que probar)
En estos 5 árboles del campus, los de sombra tuvieron hojas con área media mayor que los de sol. La causa es una respuesta de la hoja a la luz o a la temperatura.
Los árboles difieren mucho entre sí, incluso con la misma exposición. Ocurre en toda la flora del campus, en cualquier época del año.
Con 5 árboles el diseño no permite conclusiones fuertes. Las especies de sol y de sombra responden igual.

9 Limitaciones

  • Muy pocos árboles. Son 5 árboles en total (3 de sol y 2 de sombra). El valor de p más pequeño que puede dar la permutación es 0,100, y las estimaciones son muy inestables.
  • Un árbol por grupo. No se puede separar el efecto del grupo del efecto del árbol, y no se puede hacer el ANOVA de bloques.
  • No hubo asignación al azar. Los árboles ya estaban en el sol o en la sombra: es un estudio observacional. Se puede hablar de asociación, no de causa.
  • La exposición es una categoría gruesa, decidida a ojo. No se midió la luz ni (si no se anotó) la especie.
  • Hojas elegidas por personas, no al azar.
  • Error de medición. El área depende de la foto, del trazo y del script. Las decisiones de limpieza se documentaron, pero otras personas podrían haber decidido distinto.
  • Fechas distintas de medición entre árboles, y un solo periodo del año.
  • Ubicación. Las coordenadas de sl1_ están lejos del resto; hay que confirmar que son correctas antes de interpretar el mapa.
  • Supuestos de las pruebas. Con tan pocos datos, las pruebas de normalidad y de igualdad de varianzas tienen poca potencia.

10 Lista de verificación para tu informe

11 Información de la sesión

R.version.string
## [1] "R version 4.6.1 (2026-06-24 ucrt)"
Sys.Date()
## [1] "2026-11-02"