1 Contexto del experimento

Esta sección presenta el estudio para alguien que no estuvo en el campo: qué se quería saber, dónde y cuándo se midió, y con qué diseño. Lo que aparece aquí se calcula a partir de la misma base que se analiza más adelante.

1.1 Ficha del experimento

crudo <- read.csv(params$archivo, stringsAsFactors = FALSE,
                  na.strings = c("", "NA"), encoding = "UTF-8")

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 = ", "))
fechas   <- as.Date(crudo$fecha)
n_arbol  <- length(unique(toupper(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(fechas), "%d/%m/%Y"), "a", format(max(fechas), "%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_grupos, " grupos × 5 árboles = ", n_arbol, " árboles; ", nrow(crudo), " filas registradas"),
    "Celular (cámara y GPS), papel blanco, lápiz y regla, y un script 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 20/10/0023 a 20/10/0028
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 1 grupos × 5 árboles = 5 árboles; 50 filas registradas
Instrumentos Celular (cámara y GPS), papel blanco, lápiz y regla, y un script que calcula el área a partir de la foto

1.2 Sitio de estudio y protocolo

En el presente estudio se planteó hallar el área de las hojas de 5 árboles donde 2 fueron de sombra y 3 de luz. El muestreo de hojas se realizó en dos lugares, el primer muestreo se realizó en la vereda Jordancito en el municipio de Dagua (Valle del Cauca, Colombia), donde se recolectaron de 2 árboles de sol (G3_T1 Y G3_T2), 10 hojas al azar por cada árbol, registrando las coordenadas de cada árbol con un GPS. la vereda Jordancito se caracteriza por tener un clima templado que oscila entre los 16°C a los 24°C, su ambiente es húmedo con alta presencia de lluvias, este clima puede influir en las adaptaciones biológicas que poseen los árboles ubicados en esta locación, además el muestreo se realizó en un día nublado, lo que incrementa el tamaño de la hoja a comparación si se hubiera recolectado las hojas en un día soleado, pues la humedad causa que las hojas se vean más expandidas al encontrarse hidratadas. El segundo muestreo se realizó en la locación del campus de la Universidad del Valle Sede Melendez ubicado en la ciudad de Cali (Valle del Cauca, Colombia), donde se recolectaron las hojas de tres árboles, dos de sombra y uno de luz (G3_T3, G3_T4 y G3_T5), como en la locación anterior, se recolectaron 10 hojas por árbol y se registraron las coordenadas de cada árbol. La Universidad del Valle se caracteriza por tener un bosque seco, debido a sus largos periodos de sequía y las altas temperaturas que se presentan en la ciudad, oscilando éstas entre 19°C y 29°C, como en el caso anterior este clima posee ciertas condiciones beneficiosas para ciertas especies en particular que se desarrollan mejor en un ambiente seco, el muestreo de los tres arboles se realizo en un dia soleado, lo que pudo haber disminuido el tamaño de las hojas al encontrarse estas en un momento del dia donde tiene una alta pérdida de agua por transpiración de los estomas.

Finalmente se trazaron las siluetas de las hojas de cada árbol en una hoja de papel, además se dibujó un marco rectangular alrededor de la silueta, se tuvo especial cuidado en que el marco no tocase la silueta, luego se midió la distancia del marco en cm, para posteriormente fotografías las figuras y obtener el área del contorno de cada hoja en cm2.

1.3 Mapa de los sitios

Cada punto es un árbol. Haz clic en un punto para ver su foto y sus datos; arriba a la derecha puedes cambiar el fondo (se abre en satélite; también hay calles) y ocultar las hojas de sol o de sombra. Acerca el mapa: con la vista de satélite se ve si el árbol está realmente a cielo abierto o bajo otras copas, que es justo lo que define su exposición, se logra observar las dos locaciones donde fueron muestreaos las hojas de los 5 arboles

# Un registro por árbol. Solo se normaliza lo necesario para dibujar el mapa (coordenadas
# con coma y exposición con mayúsculas); la revisión completa está en la sección 4.
sitios <- unique(data.frame(grupo = crudo$grupo,
                            id_arbol = toupper(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(toupper(trimws(crudo$id_arbol)))[sitios$id_arbol])

# Foto de cada árbol: busca fotos/ID.jpg (o .jpeg, .png) y avisa si falta alguna
buscar_foto <- function(id) {
  candidatas <- file.path(params$carpeta_fotos, paste0(id, c(".jpg", ".jpeg", ".png")))
  existentes <- candidatas[file.exists(candidatas)]
  if (length(existentes) > 0) existentes[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: 5
# Texto de la ventana emergente de cada punto (la foto va incrustada en el HTML)
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)
  mapa <- leaflet(sitios) %>%
    addProviderTiles(providers$Esri.WorldImagery, group = "Satélite") %>%
    addTiles(group = "Calles") %>%
    addCircleMarkers(data = sitios[sitios$exposicion == "sol", ], lng = ~lon, lat = ~lat,
                     radius = 9, weight = 2, color = "white", fillColor = col_expo[["sol"]],
                     fillOpacity = 0.95, label = ~id_arbol, popup = ~popup,
                     popupOptions = popupOptions(maxWidth = 240), group = "Sol") %>%
    addCircleMarkers(data = sitios[sitios$exposicion == "sombra", ], lng = ~lon, lat = ~lat,
                     radius = 9, weight = 2, color = "white", fillColor = col_expo[["sombra"]],
                     fillOpacity = 0.95, label = ~id_arbol, popup = ~popup,
                     popupOptions = popupOptions(maxWidth = 240), group = "Sombra") %>%
    addLayersControl(baseGroups = c("Satélite", "Calles"), overlayGroups = c("Sol", "Sombra"),
                     options = layersControlOptions(collapsed = FALSE)) %>%
    addLegend("bottomright", colors = unname(col_expo), labels = c("Sol", "Sombra"),
              title = "Exposición", opacity = 1) %>%
    addScaleBar("bottomleft")
  mapa
} else {
  cat("El paquete 'leaflet' no está instalado: se muestra un mapa simple.",
      "Para el mapa interactivo ejecuta install.packages('leaflet').\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$grupo, cex = 0.6)
  legend("topright", legend = names(col_expo), pt.bg = col_expo, pch = 21, bty = "n")
}

1.4 Fotos de los árboles

Se fotografió cada uno de los árboles muestreados del grupo 3. En las imágenes se puede observar claramente las características morfológicas del árbol en general y su condición de sol o sombra, por otra parte son útiles como guía visual para que el lector observe las características del sitio y de la copa de cada árbol. Tres árboles de sol (G3_T1, G3_T2 y G3_T5) y dos de sombra (G3_T3 y G7_T4). Los árboles de sol reciben luz directa la mayor parte del día y no están cubiertos por otros árboles, copas ni edificaciones, los de sombra crecen bajo la copa de otros árboles o en zonas con poca luz. 3.

arb_g <- sitios[sitios$grupo == params$grupo, ]
arb_g <- arb_g[order(arb_g$id_arbol), ]

tarjetas <- vapply(seq_len(nrow(arb_g)), function(i) {
  s <- arb_g[i, ]
  img <- if (is.na(s$foto)) "<div class='sinfoto'>foto pendiente</div>"
         else paste0("<img src='", knitr::image_uri(s$foto), "'>")
  paste0("<figure>", img, "<figcaption><b>", s$id_arbol, "</b> - ", s$exposicion, "</figcaption></figure>")
}, character(1))

cat("\n\n<div class='galeria'>\n", paste(tarjetas, collapse = "\n"), "\n</div>\n\n")
G3_T1 - sol
G3_T2 - sol
G3_T3 - sombra
G3_T4 - sombra
G3_T5 - sol

2 Introducción

Las hojas son órganos especializados para el funcionamiento, crecimiento y supervivencia de los árboles, desempeñando un papel determinante en la regulación de los ciclos biogeoquímicos terrestres (Lambers et al., 2008). La lámina foliar actúa como el sitio primario de la fotosíntesis, proceso mediante el cual la radiación solar es capturada por complejos fotosintéticos y convertida en energía química (Taiz et al., 2015). Las características de las hojas como el área foliar son fundamentales para determinar la capacidad funcional de los árboles y sus respuestas adaptativas frente al entorno (Pérez-Harguindeguy et al., 2013). La superficie foliar representa la interfaz primaria de intercambio gaseoso entre la planta y la atmósfera.

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

El área de las hojas y su morfología varían según las condiciones ambientales del hábitat donde crece el árbol y sus necesidades biológicas. En ambientes sombreados los árboles desarrollan hojas de mayor área foliar y menor grosor, lo que genera una alta Área Foliar Específica (SLA). Esta morfología expande la superficie de captación de fotones en entornos donde la radiación fotosintéticamente activa es limitante (Givnish, 1988; Poorter et al., 2019). Por otro lado en ambientes soleados las hojas son más pequeñas y gruesas, es decir tiene una baja Área Foliar Específica (bajo SLA). Un área foliar reducida disminuye la capa límite de aire , lo que facilita la disipación del calor por convección y evita el sobrecalentamiento (Givnish, 1988; Taiz et al., 2015).

Por qué esperamos una diferencia. Una hoja equilibra dos cosas: captar luz y no perder demasiada agua ni calentar de más. A pleno sol sombra luz y el riesgo es el exceso de calor y de transpiración, así que se esperan hojas más pequeñas y gruesas. A la sombra la luz es el recurso escaso, para que el arbol pueda realizar su proceso de fotosíntesis aumenta el área foliar de las hojas, una lámina más grande intercepta más luz.

Hipótesis. Teniendo en cuenta la bibliografía anteriormente mencionada se espera que las hojas de los árboles que crecen a la sombra tengan, en promedio, mayor área que las de los árboles que crecen a pleno sol. Si esta afirmación fuese cierta la diferencia debería verse en los promedios por árbol y no depender de un solo grupo o de un solo árbol.

3 Métodos

Diseño. Se midieron 5 árboles, tres en sol (G3_T1, G3_T2 y G3_T5) y dos en sombra (G3_T3 y G3_T4) tomando 10 hojas por árbol. Un árbol es de sol si recibe luz directa la mayor parte del día y no lo cubren otros árboles ni edificios; en caso contrario, de sombra. Las hojas fueron adultas, completamente expandidas, sin daño evidente y repartidas por toda la copa.

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 la plantilla de la guía: 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.5.2 (2025-10-31 ucrt)):

  1. Revisión de la base y decisiones documentadas sobre cada problema encontrado.
  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: un criterio fijo (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 y se comparan esos promedios.
  6. Diseño experimental y ANOVA: de un factor entre árboles (con Tukey), anidado (hojas dentro de árboles dentro de exposición) y de bloques (grupo) con interacción y efectos simples (con Holm).

4 Datos y revisión de calidad

4.1 Lo que trae la base

head(crudo, 6)       # las primeras filas
##   grupo id_arbol     lat       lon n_hoja area_cm2 exposicion      fecha
## 1     3    G3_T1 3,52246 -76,66984      1    44,36        Sol 23/10/2026
## 2     3    G3_T1 3,52246 -76,66984      2    32,57        Sol 23/10/2026
## 3     3    G3_T1 3,52246 -76,66984      3     39,5        Sol 23/10/2026
## 4     3    G3_T1 3,52246 -76,66984      4    47,48        Sol 23/10/2026
## 5     3    G3_T1 3,52246 -76,66984      5    43,25        Sol 23/10/2026
## 6     3    G3_T1 3,52246 -76,66984      6    50,69        Sol 23/10/2026
##              observaciones
## 1                     <NA>
## 2 Hoja de la rama más baja
## 3                     <NA>
## 4                     <NA>
## 5                     <NA>
## 6                     <NA>
sapply(crudo, class) # el tipo de dato que R asignó a cada columna
##         grupo      id_arbol           lat           lon        n_hoja 
##     "integer"   "character"   "character"   "character"     "integer" 
##      area_cm2    exposicion         fecha observaciones 
##   "character"   "character"   "character"   "character"
datos <- crudo
datos$fila <- seq_len(nrow(datos))   # número de fila en el archivo (sin contar el encabezado)

# 1. ¿El ID tiene el formato G#_T#?
id_mal <- unique(datos$id_arbol[!grepl("^G[0-9]+_T[0-9]+$", datos$id_arbol)])
id_mal
## character(0)
# 2. ¿De cuántas maneras escribieron "sol" y "sombra"?
table(datos$exposicion, useNA = "ifany")
## 
##    Sol Sombra 
##     30     20

En nuestra base de datos, los identificadores de los árboles cumplieron con el formato establecido (sin IDs en minúsculas). Sin embargo, la exposición aparecía registrada con la primera letra en mayúscula (“Sol” y “Sombra”). Para R, “Sol”, “SOL” y “sol” se tratarían como categorías distintas en una prueba o un gráfico. Por lo tanto, se normalizaron los textos a minúsculas y se eliminaron los espacios adicionales mediante el siguiente bloque de código:

# ---- Limpieza y validación de datos ------------------------------------------

datos <- crudo

# Número de fila original del archivo
datos$fila <- seq_len(nrow(datos))

# 1. Normalizar identificadores y exposición
datos$id_arbol <- toupper(trimws(as.character(datos$id_arbol)))
datos$exposicion <- tolower(trimws(as.character(datos$exposicion)))

# 2. Contar registros normalizados
n_id_corregido <- sum(
  !is.na(crudo$id_arbol) &
    crudo$id_arbol != datos$id_arbol
)

n_expo_corregida <- sum(
  !is.na(crudo$exposicion) &
    crudo$exposicion != datos$exposicion
)

cat("IDs normalizados:", n_id_corregido, "\n")
## IDs normalizados: 0
cat("Exposiciones normalizadas:", n_expo_corregida, "\n")
## Exposiciones normalizadas: 50
# 3. Comprobar formato de los identificadores
patron_id <- "^G[0-9]+_T[0-9]+$"

id_mal <- unique(
  datos$id_arbol[
    is.na(datos$id_arbol) |
      !grepl(patron_id, datos$id_arbol)
  ]
)

if (length(id_mal) > 0) {
  print(id_mal)
  stop("Hay identificadores que no cumplen el formato G#_T#.")
} else {
  message("Todos los identificadores tienen el formato correcto.")
}

# 4. Comprobar categorías de exposición
print(table(datos$exposicion, useNA = "ifany"))
## 
##    sol sombra 
##     30     20
expo_mal <- unique(
  datos$exposicion[
    is.na(datos$exposicion) |
      !datos$exposicion %in% c("sol", "sombra")
  ]
)

if (length(expo_mal) > 0) {
  print(expo_mal)
  stop("Hay categorías de exposición incorrectas o faltantes.")
}

# 5. Extraer identificadores individuales de los árboles
# G3_T1 -> árbol 1
# G3_T2 -> árbol 2, etc.

datos$numero_arbol <- as.integer(
  sub("^G[0-9]+_T([0-9]+)$", "\\1", datos$id_arbol)
)


# 7. Comprobar que cada árbol tenga una sola exposición
expo_por_arbol <- tapply(
  datos$exposicion,
  datos$id_arbol,
  function(x) length(unique(x[!is.na(x)]))
)

arboles_expo_mal <- names(expo_por_arbol)[
  expo_por_arbol != 1
]

if (length(arboles_expo_mal) > 0) {
  print(arboles_expo_mal)
  stop("Hay árboles registrados con más de una exposición o sin exposición.")
}


# 9. Resumen final
cat("\n--- RESUMEN DE VALIDACIÓN ---\n")
## 
## --- RESUMEN DE VALIDACIÓN ---
cat("Número de registros:", nrow(datos), "\n")
## Número de registros: 50
cat("Número de árboles:", length(unique(datos$id_arbol)), "\n")
## Número de árboles: 5
cat("IDs incorrectos:", length(id_mal), "\n")
## IDs incorrectos: 0
cat("Exposiciones incorrectas:", length(expo_mal), "\n")
## Exposiciones incorrectas: 0
message("Validación de datos completada correctamente.")

4.2 Revisiones

Durante la importación de los datos, se identificó que los registros de latitud y longitud empleaban la coma decimal (,), lo que ocasionaba que R los interpretara como texto (character) en lugar de valores numéricos. Para corregir esta inconsistencia y permitir su procesamiento espacial, se utilizó la función auxiliar a_numero() para sustituir las comas por puntos y convertir la columna a formato numérico (datos\(lat <- a_numero(datos\)lat))

# 3. Coordenadas: ¿qué grupos escribieron coma decimal?
grupos_coma <- sort(unique(crudo$grupo[grepl(",", crudo$lat) | grepl(",", crudo$lon)]))
grupos_coma
## [1] 3
datos$lat <- a_numero(datos$lat)
datos$lon <- a_numero(datos$lon)
if (!is.numeric(datos$area_cm2)) datos$area_cm2 <- a_numero(datos$area_cm2)

summary(datos[, c("lat", "lon")], digits = 7)   # todas deben caer en la zona del campus
##       lat               lon           
##  Min.   :3,37658   Min.   :-76,66984  
##  1st Qu.:3,37678   1st Qu.:-76,66912  
##  Median :3,52246   Median :-76,66832  
##  Mean   :3,46434   Mean   :-76,61457  
##  3rd Qu.:3,52277   3rd Qu.:-76,53294  
##  Max.   :3,52311   Max.   :-76,53261
# 4. Áreas faltantes o no válidas (la plantilla pide área > 0)
datos[is.na(datos$area_cm2) | datos$area_cm2 <= 0,
      c("fila", "id_arbol", "n_hoja", "area_cm2", "observaciones")]
##    fila id_arbol n_hoja area_cm2 observaciones
## 30   30    G3_T3     10       NA          <NA>
# 5. Filas repetidas (mismo árbol y mismo número de hoja) y n_hoja fuera de 1-10
repetida <- duplicated(datos[, c("id_arbol", "n_hoja")])
datos[repetida | duplicated(datos[, c("id_arbol", "n_hoja")], fromLast = TRUE),
      c("fila", "id_arbol", "n_hoja", "area_cm2")]
## [1] fila     id_arbol n_hoja   area_cm2
## <0 rows> (o 0- extensión row.names)
range(datos$n_hoja)
## [1]  1 10
# 6. ¿Cuántas hojas tiene cada árbol (sin contar las repetidas)?
n_por_arbol <- table(datos$id_arbol[!repetida])
n_por_arbol[n_por_arbol != 10]
## named integer(0)
# 7. ¿Cada grupo tiene al menos 2 árboles de sol y 2 de sombra?
table(unique(datos[, c("grupo", "id_arbol", "exposicion")])[, c("grupo", "exposicion")])
##      exposicion
## grupo sol sombra
##     3   3      2
# 8. Valores extremos respecto a su propio árbol: área / mediana del á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 <- datos[!is.na(datos$razon_mediana) & datos$area_cm2 > 0 &
                    (datos$razon_mediana > 10 | datos$razon_mediana < 0.1),
                  c("fila", "id_arbol", "n_hoja", "area_cm2", "razon_mediana")]
extremos$razon_mediana <- round(extremos$razon_mediana, 1)
extremos
## [1] fila          id_arbol      n_hoja        area_cm2      razon_mediana
## <0 rows> (o 0- extensión row.names)
tabla(datos[!is.na(datos$observaciones),
            c("fila", "id_arbol", "n_hoja", "area_cm2", "observaciones")], row.names = FALSE)
fila id_arbol n_hoja area_cm2 observaciones
2 G3_T1 2 32,57 Hoja de la rama más baja
15 G3_T2 5 84,83 Hoja atacada por hongos

4.3 Decisiones

Cada problema se resuelve con una regla explícita, y la fila original se conserva 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)
}

danadas <- grepl("mordid|insect|hongo|dañ|roto|rota|herbiv", datos$observaciones, ignore.case = TRUE)

n_repetidas <- marcar(repetida, "fila repetida")
n_sin_area  <- marcar(is.na(datos$area_cm2), "sin área")
n_cero      <- marcar(!is.na(datos$area_cm2) & datos$area_cm2 <= 0, "área no válida (<= 0)")
n_danadas   <- marcar(!is.na(danadas) & danadas, "hoja dañada (el protocolo pide hojas sin daño)")


limpios <- datos[datos$usar, ]
limpios <- limpios[order(limpios$grupo, as.integer(sub(".*_T", "", limpios$id_arbol)), limpios$n_hoja), ]
decisiones <- data.frame(
  Problema = c("Exposición escrita de varias formas",
               "Coordenadas con coma decimal", "Área faltante",
               "Hoja dañada por insectos"),
  Filas = c(n_expo_corregida,
            sum(crudo$grupo %in% grupos_coma), n_sin_area, n_danadas),
  Decisión = c("Pasar a minúsculas y quitar espacios",
               "Reemplazar la coma por punto y convertir a número",
               "Excluir la hoja (no hay dato)",
               "Excluir: el protocolo pide hojas sin daño evidente"))
tabla(decisiones, align = "lrl")
Problema Filas Decisión
Exposición escrita de varias formas 50 Pasar a minúsculas y quitar espacios
Coordenadas con coma decimal 50 Reemplazar la coma por punto y convertir a número
Área faltante 1 Excluir la hoja (no hay dato)
Hoja dañada por insectos 1 Excluir: el protocolo pide hojas sin daño evidente
names(limpios)
##  [1] "grupo"             "id_arbol"          "lat"              
##  [4] "lon"               "n_hoja"            "area_cm2"         
##  [7] "exposicion"        "fecha"             "observaciones"    
## [10] "fila"              "numero_arbol"      "razon_mediana"    
## [13] "area_cm2_original" "usar"              "motivo"           
## [16] "corregida"
cat("Filas originales:", nrow(crudo), "\n",
    "Filas que se usan:", nrow(limpios), "\n",
    "Árboles distintos:", length(unique(limpios$arbol_unico)), "\n")
## Filas originales: 50 
##  Filas que se usan: 48 
##  Árboles distintos: 0
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 = "",)
}

Al aplicar la regla de validación para identificar posibles errores de escala (razón de área mayor a 10 veces la mediana del árbol), la operación no arrojó ningún registro activo (numeric(0)). Esto confirma que el error de unidades detectado previamente en el conjunto de datos fue corregido de manera exitosa, dejando el registro dentro del rango normal esperado para su respectivo espécimen.

Base de datos limpia : "datos_limpios_area_foliar.csv"

5 Resultados

g  <- params$grupo
stopifnot(g %in% limpios$grupo)
dg <- limpios[limpios$grupo == g, ]

5.1 Estadística descriptiva por árbol

Tras la depuración de la base de datos (exclusión de 1 hoja con daño por depredacion de insectos en G3_T2 y 1 registro sin dato de área en G3_T3), el conjunto de datos activo consta de \(n = 48\) hojas distribuidas en 5 árboles del Grupo 3 (\(n_{\text{sol}} = 3\) árboles, \(n_{\text{sombra}} = 2\) árboles).

resumen <- do.call(rbind, lapply(split(dg, dg$id_arbol), function(d) {
  data.frame(arbol = 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))
}))
resumen <- resumen[order(as.integer(sub(".*_T", "", resumen$arbol))), ]

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.
G3_T1 sol 10 42,1 42,3 5,1 12,1 32,6 50,7
G3_T2 sol 9 81,7 81,5 12,5 15,3 60,3 98,7
G3_T3 sombra 9 136,9 139,8 20,3 14,8 99,5 170,6
G3_T4 sombra 10 143,8 148,9 40,1 27,9 64,5 187,0
G3_T5 sol 10 121,5 122,5 21,9 18,0 87,2 145,9

5.2 Un diagrama de cajas por árbol

dg$id_arbol <- factor(dg$id_arbol, levels = resumen$arbol)
par(mar = c(4.5, 4.8, 3, 1))
boxplot(area_cm2 ~ id_arbol, data = dg, outline = FALSE, las = 1,
        col = adjustcolor(col_expo[resumen$exposicion], 0.55), border = "grey25",
        xlab = "Árbol", ylab = expression("Área foliar (cm"^2*")"),
        main = paste("Grupo", g, "- área foliar por árbol"))
stripchart(area_cm2 ~ id_arbol, data = dg, vertical = TRUE, method = "jitter", jitter = 0.12,
           pch = 16, col = adjustcolor("black", 0.45), add = TRUE)
legend("topleft", 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.

En el diagram de cajas y bigotes se puede observar que los árboles T1 y T2 presentan la menor área foliar general, ademas de ser lo valores con menor dispersión de los 3 gráficos, por otro lado el árbol que tuvo más dispersión fue el del árbol T4 presentando un valor atípico muy por debajo del resto, este valor atípico se genero al colectar una hoja que no se contraba en la copa (por su difícil acceso), siendo esta una hoja inmadura y por lo tanto más pequeña que el resto, lo que generó la gran dispersión y ese valor atipico tan incongruente. En general se puede ver que las medianas de los dos árboles de sombra son superiores a las medianas de los 3 árboles de sol, lo afirma la hipótesis planteada con los datos estadísticos obtenidos en el experimento.

afectados <- unique(dg$id_arbol[dg$corregida])
for (a in afectados) {
  x0 <- dg$area_cm2_original[dg$id_arbol == a]
  x1 <- dg$area_cm2[dg$id_arbol == a]
  cat(sprintf("%s: media %s -> %s cm2 ; CV %s %% -> %s %%\n", a,
              fmt(mean(x0)), fmt(mean(x1)), fmt(cv(x0), 0), fmt(cv(x1), 0)))
}
if (length(afectados) == 0) cat("En este grupo no hubo errores de unidad.\n")
## En este grupo no hubo errores de unidad.

5.3 Valores atípicos y sensibilidad

Se evaluó la presencia de valores atípicos mediante la regla fija de Tukey:

1,5 x RIC alrededor del rango intercuartilico dentro de cada árbol

fuera <- do.call(rbind, lapply(split(dg, dg$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
G3_T2 1 60,34 NA
G3_T2 7 67,64 NA
G3_T2 8 98,41 NA
G3_T2 9 98,66 NA
G3_T3 2 99,54 NA

Ninguna hoja limpia en el conjunto de 48 datos traspasa los límites de Tukey calculados individualmente para el árbol.

Al comparar la diferencia global (Sombra - Sol) aplicando distintos indicadores de tendencia central por árbol se obtuvieron los siguientes resultados:

sens <- do.call(rbind, lapply(split(dg, dg$id_arbol), function(d) {
  out <- boxplot.stats(d$area_cm2)$out
  data.frame(arbol = 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 = "media", mediana = "mediana", media_sin_atipicas = "media_sin_atipicas"),
                   function(v) mean(sens[[v]][sens$exposicion == "sombra"]) -
                               mean(sens[[v]][sens$exposicion == "sol"]))

tabla(data.frame(Resumen_por_arbol = c("Media (todas las hojas)", "Mediana",
                                       "Media sin las hojas atípicas"),
                 Diferencia_sombra_menos_sol = 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) 58,6
Mediana 62,2
Media sin las hojas atípicas 60,8

La concordancia casi perfecta entre estimadores demuestra que los resultados no están distorsionados por anomalías de medición o valores puntuales ruidosos.

5.4 Diagrama de cajas por árbol

dg$expo <- factor(dg$exposicion, levels = c("sol", "sombra"))
simbolo <- c(16, 17, 15, 18, 8)[as.integer(dg$id_arbol)]
par(mar = c(4.5, 4.8, 3, 1))
boxplot(area_cm2 ~ expo, data = dg, outline = FALSE, las = 1,
        col = adjustcolor(col_expo, 0.5), border = "grey25",
        xlab = "Exposición", ylab = expression("Área foliar (cm"^2*")"),
        main = paste("Grupo", g, "- hojas por exposición"))
set.seed(2)   # para que el "jitter" (separación horizontal de los puntos) sea siempre el mismo
points(as.integer(dg$expo) + runif(nrow(dg), -0.15, 0.15), dg$area_cm2,
       pch = simbolo, col = adjustcolor("black", 0.6))
legend("topleft", legend = levels(dg$id_arbol), pch = c(16, 17, 15, 18, 8), bty = "n",
       title = "Árbol", cex = 0.85)
Hojas de sol y de sombra del grupo. Cada símbolo es un árbol distinto.

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

Se realizaron dos boxplot respecto al tipo de exposición de todos los árboles para tener pruebas más sólidas que respalden la hipótesis, al igual que en el gráfico anterior los árboles expuestos a la sombra presentaron una distribución general y mediana mayor a los árboles expuestos al sol. se observa una diferencia clara pero no grande, ya que el valor atípico del árbol T3 genera un límite menos muy bajo, inclusive más por debajo de la mediana o el Q3 de el boxplot a los árboles expuestos directamente al sol.

5.5 Árbol como unidad de análisis

Lo que se ha asignado 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_g <- aggregate(area_cm2 ~ id_arbol + exposicion, data = dg, FUN = mean)
medias_g$exposicion <- factor(medias_g$exposicion, levels = c("sombra", "sol"))
medias_g <- medias_g[order(medias_g$exposicion, medias_g$id_arbol), ]

n_expo_g <- table(medias_g$exposicion)
n_expo_g
## 
## sombra    sol 
##      2      3
tabla(medias_g, row.names = FALSE, digits = 1,
      col.names = c("Árbol", "Exposición", "Media del árbol (cm²)"))
Árbol Exposición Media del árbol (cm²)
G3_T3 sombra 136,9
G3_T4 sombra 143,8
G3_T1 sol 42,1
G3_T2 sol 81,7
G3_T5 sol 121,5
dif_g <- mean(medias_g$area_cm2[medias_g$exposicion == "sombra"]) -
         mean(medias_g$area_cm2[medias_g$exposicion == "sol"])
dif_g
## [1] 58,59706

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

Con tan pocos árboles se puede hacer algo mejor que suponer una distribución: una prueba de permutación exacta. Si la exposición no importara, cualquiera de las formas de repartir las etiquetas “sol” y “sombra” entre los árboles sería igual de probable

y        <- medias_g$area_cm2
es_sombra <- medias_g$exposicion == "sombra"
n_som    <- sum(es_sombra)

repartos <- combn(length(y), n_som)                         # todas las formas de elegir los árboles de sombra
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)

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 del grupo.

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

El gráfico de dispersión horizontal ubica las 10 diferencias de medias posibles entre −72,20  y +58,60. El punto correspondiente al reparto realmente observado (+58,60, resaltado en color azul) se sitúa en el extremo derecho absoluto del eje.

5.5.2 Lo que habría pasado ignorando el árbol

La comparación entre la prueba ingenua (hojas como réplicas) y el análisis riguroso (árbol como unidad) resalta el impacto de la estructura jerárquica

t_hojas <- t.test(area_cm2 ~ expo, data = dg)                 # trata las hojas como independientes
p_hojas <- t_hojas$p.value
comparacion_g <- data.frame(
  Análisis = c("Hojas como datos independientes (incorrecto)", "Un promedio por árbol (permutación exacta)"),
  n_sol = c(sum(dg$expo == "sol"), sum(!es_sombra)),
  n_sombra = c(sum(dg$expo == "sombra"), n_som),
  p = c(fmt_p(p_hojas), fmt_p(p_perm)))
tabla(comparacion_g, col.names = c("Análisis", "n sol", "n sombra", "p"), align = "lrrr")
Análisis n sol n sombra p
Hojas como datos independientes (incorrecto) 29 19 < 0,001
Un promedio por árbol (permutación exacta) 3 2 0,200

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

Hasta aquí comparamos sol y sombra con promedios por árbol y pruebas t, Wilcoxon y de permutación. El análisis de varianza (ANOVA) generaliza esa comparación y permite usar la estructura completa del diseño. En esta sección se responden tres preguntas, cada una con su herramienta:

Pregunta Herramienta Después del ANOVA
¿Difieren entre sí los árboles de un grupo? 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 experimental

La actividad es un estudio observacional con estructura jerárquica. Cada elemento del diseño tiene un papel:

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

Con esos elementos, el modelo completo es

log(área) ~ exposición + árbol(exposición) + error de hoja

El término árbol(exposición) (árboles dentro de cada combinación) es el error experimental contra el que se prueba la exposición. Si ese término se ignora y se usa el error de las hojas, se cae en la pseudorreplicación.

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 = dg, 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,8633068       0,5978087

Entre árboles, la desviación estándar crece con la media en la escala original (correlación 0,86), y esa relación casi desaparece con el logaritmo (0,60). Esto cumple mejor el supuesto de varianzas parecidas que exige el ANOVA.

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

Se compara el log del área de las hojas entre los 5 árboles del grupo. Aquí las hojas sí son réplicas de cada árbol, y la pregunta es por esos árboles concretos, no por la exposición.

dg$id_arbol <- factor(dg$id_arbol)
m1 <- lm(log(area_cm2) ~ id_arbol, data = dg)
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), hojas del grupo.")
ANOVA de un factor sobre log(área), hojas del grupo.
Fuente gl SC CM F p
Entre árboles 4 10,017 2,504 59,19 < 0,001
Dentro de árboles (hojas) 43 1,819 0,042

Existe una heterogeneidad altamente significativa entre los árboles del grupo F(4; 43) = 59,2, p < 0,001.

6.2.1 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).

shap1 <- shapiro.test(resid(m1))
flig1 <- fligner.test(log(area_cm2) ~ id_arbol, data = dg)
kw1   <- kruskal.test(log(area_cm2) ~ id_arbol, data = dg)     # alternativa sin supuesto de normalidad
c(shapiro_p = shap1$p.value, fligner_p = flig1$p.value, kruskal_p = kw1$p.value)
##      shapiro_p      fligner_p      kruskal_p 
## 0,004620119871 0,059786339626 0,000000475052

En el panel izquierdo los residuos se distribuyen de manera homogénea alrededor de la línea horizontal de cero en los 5 niveles ajustados, los cuales corresponen a los 5 árboles. La uniformidad de la extensión vertical de los puntos se revalida con la prueba de Fligner-Killeen p = 0,060

En el panel derecho (Normal Q-Q Plot), se observa que la mayoría de los cuantiles de los residuos coinciden con la recta teórica de normalidad 1:1 en el tramo central. Sin embargo, en el extremo inferior de la cola izquierda se presenta una ligera desviación hacia abajo, ocasionada por un par de hojas de menor tamaño correspondientes a G3_T1 y G3_T4. Aunque la prueba de Shapiro-Wilk resulta estadísticamente significativa p = 0,005, lo que indica una desviación respecto a la normalidad, la prueba no paramétrica de Kruskal-Wallis ratifica de manera consistente las diferencias entre árboles p = < 0,001.

6.3 Post-ANOVA (Tukey HSD)

Con 5 árboles hay 10 comparaciones por pares. Si se hicieran 10 pruebas t sueltas al 5 %, la probabilidad de al menos un falso positivo sería de alrededor de 40 %. La prueba de Tukey HSD compara todos los pares y controla ese error para el conjunto completo.

tk <- TukeyHSD(aov(log(area_cm2) ~ id_arbol, data = dg))$id_arbol
tk_tab <- data.frame(comparacion = gsub("-", " / ", 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), ]

# ¿Cuántos pares que difieren son de árboles con la MISMA exposición?
pares_ids <- strsplit(tk_tab$comparacion, " / ")
misma_expo <- sapply(pares_ids, function(x) length(unique(resumen$exposicion[match(x, resumen$arbol)])) == 1)
n_misma_sig <- sum(misma_expo & tk_tab$p_aj < 0.05)

tabla_tk <- data.frame(tk_tab$comparacion, fmt(tk_tab$razon, 2),
                       paste0("[", fmt(tk_tab$li, 2), "; ", fmt(tk_tab$ls, 2), "]"),
                       fmt_p(tk_tab$p_aj), ifelse(tk_tab$p_aj < 0.05, "sí", "no"))
tabla(tabla_tk, col.names = c("Comparación (A / B)", "Razón A / B", "IC 95 % (Tukey)", "p ajustado", "Difieren (5 %)"),
      align = "lrrrr", caption = "Comparaciones de Tukey entre árboles del grupo (escala logarítmica, mostradas como razones).")
Comparaciones de Tukey entre árboles del grupo (escala logarítmica, mostradas como razones).
Comparación (A / B) Razón A / B IC 95 % (Tukey) p ajustado Difieren (5 %)
G3_T4 / G3_T1 3,29 [2,53; 4,28] < 0,001 sí
G3_T3 / G3_T1 3,24 [2,48; 4,24] < 0,001 sí
G3_T5 / G3_T1 2,86 [2,20; 3,72] < 0,001 sí
G3_T2 / G3_T1 1,93 [1,48; 2,53] < 0,001 sí
G3_T4 / G3_T2 1,70 [1,30; 2,23] < 0,001 sí
G3_T3 / G3_T2 1,68 [1,27; 2,21] < 0,001 sí
G3_T5 / G3_T2 1,48 [1,13; 1,94] 0,001 sí
G3_T5 / G3_T4 0,87 [0,67; 1,13] 0,552 no
G3_T5 / G3_T3 0,88 [0,67; 1,16] 0,684 no
G3_T4 / G3_T3 1,02 [0,78; 1,33] 1,000 no
k <- nrow(tk_tab)
par(mar = c(4.5, 9, 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 = 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 7 de las 10 comparaciones los árboles difieren de forma detectable (en naranja). Cruza esto con la exposición de cada árbol (tabla de la sección 5): 3 de esas diferencias son entre árboles de la misma exposición. Es decir, dos árboles a la sombra pueden diferir tanto como un árbol de sol y uno de sombra.

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.

lim <- limpios
lim$arbol <- factor(lim$id_arbol)
lim$expo2 <- factor(lim$exposicion, levels = c ("sol", "sombra"))


m_anid <- lm(log(area_cm2) ~ expo2 + arbol, data = lim)       
a_an   <- tabla_anova(m_anid)


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)

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 4,375 4,375 CM de árbol 2,33 0,225
Árbol (dentro de exposición) 3 5,641 1,880 CM de hojas 44,45 < 0,001
Hojas (residual) 43 1,819 0,042
p_ingenuo <- a_an$p[1]     # prueba "ingenua": exposición contra el CM de las hojas
  • Exposición: F(1; 3) = 2,33, p = 0,225: con el árbol como unidad hay diferencia entre sol y sombra.
  • Árbol dentro de exposición: p < 0,001. Árboles de la misma exposición difieren mucho entre sí; ese es el ruido contra el que debe competir el efecto de la exposición.
  • 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.

¿Cuánto de la variación se debe al árbol y cuánto a las hojas?

n0     <- nrow(lim) / nlevels(lim$arbol)                    # hojas por árbol (promedio)
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,19147509  0,04230825 81,90279418

Alrededor del 82 % de la varianza total del sistema radica en las diferencias entre árboles mientras que solo el 0 .

7 Discusión

La hipótesis nula planteaba que no existen diferencias en el área foliar promedio entre los árboles expuestos al sol y a la sombra, mientras que la hipótesis alternativa proponía que los árboles ubicados en condiciones de sombra desarrollarían hojas significativamente más grandes para favorecer la captación de luz. A nivel descriptivo, los resultados coinciden cualitativamente con esta hipótesis biológica, ya que los dos árboles de sombra registraron los promedios de área foliar más altos. Sin embargo, desde el punto de vista de la inferencia estadística, no es posible aceptar formalmente la hipótesis alternativa, debido a que existe un solapamiento considerable entre las distribuciones. Esto se evidencia particularmente en el árbol de sol G3_T5, que presentó un área foliar media de 121,52 cm², lo que impide demostrar un efecto sistemático de la exposición lumínica independiente de la identidad del árbol. Además, el análisis permitió identificar posibles variables no controladas que pudieron influir en los resultados, especialmente la confusión por sitio y microclima. Los árboles G3_T1 y G3_T2 fueron recolectados en la Vereda Jordancito (Dagua), una zona de clima templado-húmedo, con temperaturas entre 16 °C y 24 °C, durante un día nublado. Por otro lado, los árboles G3_T3, G3_T4 y G3_T5 proceden del Campus Meléndez (Cali), caracterizado por condiciones de bosque seco tropical, con temperaturas entre 19 °C y 29 °C y una mayor evapotranspiración. Esta diferencia entre localidades resulta relevante, ya que el árbol de sol G3_T5, ubicado en Cali, presentó un área foliar aproximadamente tres veces mayor que la del árbol de sol G3_T1, ubicado en Dagua. Este resultado sugiere que factores como la disponibilidad de humedad en el suelo, las condiciones microclimáticas y las características particulares de cada individuo pudieron tener una mayor influencia sobre el área foliar que la propia exposición solar, por lo que es necesario considerar estas variables en futuras investigaciones para evaluar con mayor precisión el efecto de la luz sobre el desarrollo de las hojas.

8 Limitaciones

  • Pocos árboles. Son árboles en total, y 5 por grupo. Las estimaciones de un solo grupo son muy inestables.
  • 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 que la sombra cause hojas más grandes.
  • La exposición es una categoría gruesa, decidida por cada grupo. No se midió la luz (por ejemplo, con una aplicación o un fotómetro) ni se registró la especie.
  • Hojas elegidas por personas. Aunque la guía pide repartirlas por toda la copa, no se eligieron al azar.
  • Error de medición. El área depende de la foto, del trazo y del script; 2 de las 50 filas no se usaron (una de ellas por estar repetida) y una hoja se corrigió. Las decisiones de limpieza se documentaron, pero otras personas podrían haber decidido distinto.
  • Un solo momento. Las hojas se midieron en pocos días de octubre; el área puede cambiar con la época del año y con el estado hídrico del árbol.
  • Comparaciones múltiples. Los efectos simples por grupo (8 pruebas) y las comparaciones entre árboles (10 pares) se ajustaron con Holm y Tukey; los resultados por grupo son exploratorios.
  • Datos simulados. Nada de lo anterior describe árboles reales del campus.

9 Información de la sesión

R.version.string
## [1] "R version 4.5.2 (2025-10-31 ucrt)"
Sys.Date()
## [1] "2026-11-04"