1 Introducción

Pregunta. ¿Cómo cambia el área de las hojas según el lugar donde crece el árbol? Comparamos árboles del campus según su exposición a la luz: sol (luz directa la mayor parte del día, sin sombra de otros árboles ni edificios) o sombra (en caso contrario).

Hipótesis (escrita antes de salir a campo). Los árboles expuestos al sol tienen hojas de mayor área que los de sombra, porque con más luz captan más energía y fijan más carbono para expandir la lámina foliar. La hipótesis nula es que el área foliar no difiere entre ambas exposiciones.

2 Métodos

2.1 Muestreo

  • Árboles: 5 árboles (3 a pleno sol y 2 a la sombra), identificados como G#_T# y georreferenciados en grados decimales. No fue posible identificar la especie o el morfotipo de ninguno.
  • Hojas: 10 por árbol (50 en total), recolectadas el 22/10/2026, adultas y ya desarrolladas (punto de mayor crecimiento), elegidas al azar de diferentes ramas, numeradas del 1 al 10 y medidas el mismo día.

2.2 Medición del área

Se trazó el contorno de cada hoja a lápiz sobre papel blanco y se dibujó con regla un marco rectangular cuyos lados se midieron en mm. El papel se fotografió y el área (cm²) se estimó usando enteramente un código brindado por el docente de la asignatura, que corrige la perspectiva usando el marco como escala.

2.3 Análisis estadístico

  1. Revisión de la base (formato, faltantes, duplicados, coordenadas).
  2. Estadística descriptiva por árbol: n, media, mediana, desviación estándar y coeficiente de variación (CV = sd / media × 100).
  3. Boxplots por árbol y por exposición.
  4. Valores atípicos con dos criterios (Tukey y z modificado) y decisión justificada.
  5. Comparación sol vs sombra con el promedio por árbol como unidad de análisis, ya que las 10 hojas de un árbol no son independientes: prueba t de Welch y, sin supuesto de normalidad, prueba exacta de Wilcoxon–Mann–Whitney.

3 Resultados

3.1 Base de datos

ARCHIVO <- "hojas - diseño - tabla de datos.csv"
SALIDA  <- "base_datos_area_foliar_revisada.csv"

if (!file.exists(ARCHIVO)) {
  stop("No encuentro '", ARCHIVO, "'. Pon el .Rmd y el CSV en la misma carpeta ",
       "(en RStudio: Session > Set Working Directory > To Source File Location).", call. = FALSE)
}

# Todo se lee como texto y se convierte después, para controlar cada columna.
crudo <- read.csv(ARCHIVO, colClasses = "character", check.names = FALSE,
                  fileEncoding = "UTF-8-BOM")
names(crudo) <- tolower(trimws(names(crudo)))   # la guía pide nombres en minúscula
crudo[] <- lapply(crudo, trimws)

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)

# Separador decimal mixto ("51,63" y "51.64"): se cuenta antes de unificar
n_punto <- sum(grepl(".", crudo$area_cm2, fixed = TRUE))

# "3,377" y "3.377" -> 3.377 ; si algo no es numérico, se detiene con un mensaje claro
num <- function(v, nombre) {
  r <- suppressWarnings(as.numeric(gsub(",", ".", v, fixed = TRUE)))
  if (any(is.na(r) & v != "")) stop("La columna '", nombre, "' tiene valores no numéricos.", call. = FALSE)
  r
}

datos <- data.frame(
  grupo         = as.integer(num(crudo$grupo, "grupo")),
  id_arbol      = crudo$id_arbol,
  lat           = num(crudo$lat, "lat"),
  lon           = num(crudo$lon, "lon"),
  n_hoja        = as.integer(num(crudo$n_hoja, "n_hoja")),
  area_cm2      = num(crudo$area_cm2, "area_cm2"),
  exposicion    = tolower(crudo$exposicion),
  fecha         = as.Date(crudo$fecha, tryFormats = c("%d/%m/%Y", "%Y-%m-%d")),
  observaciones = ifelse(crudo$observaciones == "", NA_character_, crudo$observaciones),
  stringsAsFactors = FALSE
)
haversine <- function(lat1, lon1, lat2, lon2) {          # distancia en metros entre dos puntos GPS
  rad <- pi / 180; R <- 6371000
  a <- sin((lat2 - lat1) * rad / 2)^2 +
       cos(lat1 * rad) * cos(lat2 * rad) * sin((lon2 - lon1) * rad / 2)^2
  2 * R * asin(sqrt(a))
}
coords <- unique(datos[, c("id_arbol", "lat", "lon")])
D <- outer(seq_len(nrow(coords)), seq_len(nrow(coords)),
           function(i, j) haversine(coords$lat[i], coords$lon[i], coords$lat[j], coords$lon[j]))
dimnames(D) <- list(coords$id_arbol, coords$id_arbol)
dist_min <- min(D[upper.tri(D)])

arboles_exp <- unique(datos[, c("id_arbol", "exposicion")])

chequeos <- list()
revisar <- function(descripcion, ok) {
  chequeos[[length(chequeos) + 1]] <<- data.frame(
    revision = descripcion, cumple = ifelse(isTRUE(ok), "sí", "NO"))
  invisible(ok)
}

sin_na <- setdiff(names(datos), "observaciones")
revisar("Sin valores faltantes (excepto 'observaciones')", !anyNA(datos[, sin_na]))
revisar("El ID tiene el formato G#_T#", all(grepl("^G[0-9]+_T[0-9]+$", datos$id_arbol)))
revisar("El número del grupo coincide con el ID",
        isTRUE(all(suppressWarnings(as.integer(sub("^G([0-9]+)_T.*$", "\\1", datos$id_arbol))) == datos$grupo)))
revisar("Hay 5 árboles", length(unique(datos$id_arbol)) == 5)
revisar("Cada árbol tiene exactamente 10 hojas", all(table(datos$id_arbol) == 10))
revisar("n_hoja va de 1 a 10 sin repetirse dentro de cada árbol",
        all(tapply(datos$n_hoja, datos$id_arbol, function(v) identical(sort(v), 1:10))))
revisar("Todas las áreas son números mayores que 0", all(is.finite(datos$area_cm2) & datos$area_cm2 > 0))
revisar("exposicion solo toma los valores 'sol' o 'sombra'", all(datos$exposicion %in% c("sol", "sombra")))
revisar("Cada árbol tiene una sola exposición", all(tapply(datos$exposicion, datos$id_arbol, function(v) length(unique(v))) == 1))
revisar("Cada árbol tiene un solo par de coordenadas",
        all(tapply(paste(datos$lat, datos$lon), datos$id_arbol, function(v) length(unique(v))) == 1))
revisar("Hay una sola fecha de recolección y es válida", !anyNA(datos$fecha) && length(unique(datos$fecha)) == 1)
revisar("Al menos 2 árboles a pleno sol y 2 a la sombra",
        sum(arboles_exp$exposicion == "sol") >= 2 && sum(arboles_exp$exposicion == "sombra") >= 2)
revisar("Ningún par de árboles a menos de 5 m (descarta un árbol medido dos veces)", dist_min > 5)

tabla_chequeos <- do.call(rbind, chequeos)
knitr::kable(tabla_chequeos, col.names = c("Revisión", "¿Cumple?"),
             caption = "Revisión de la base de datos")
Revisión de la base de datos
Revisión ¿Cumple?
Sin valores faltantes (excepto ‘observaciones’) sí
El ID tiene el formato G#_T# sí
El número del grupo coincide con el ID sí
Hay 5 árboles sí
Cada árbol tiene exactamente 10 hojas sí
n_hoja va de 1 a 10 sin repetirse dentro de cada árbol sí
Todas las áreas son números mayores que 0 sí
exposicion solo toma los valores ‘sol’ o ‘sombra’ sí
Cada árbol tiene una sola exposición sí
Cada árbol tiene un solo par de coordenadas sí
Hay una sola fecha de recolección y es válida sí
Al menos 2 árboles a pleno sol y 2 a la sombra sí
Ningún par de árboles a menos de 5 m (descarta un árbol medido dos veces) sí
if (any(tabla_chequeos$cumple == "NO")) {
  stop("Hay revisiones que no se cumplen; corrige la base antes de continuar.", call. = FALSE)
}

La base pasó las 13 revisiones. Un valor de área (1 en total). La distancia mínima entre árboles es de 26 m.

# Se ordenan los árboles (sol primero) para las gráficas y se guarda la base revisada
datos$exposicion <- factor(datos$exposicion, levels = c("sol", "sombra"))
orden <- unique(as.character(datos$id_arbol[order(datos$exposicion, datos$id_arbol)]))
datos$id_arbol <- factor(datos$id_arbol, levels = orden)
datos <- datos[order(datos$id_arbol, datos$n_hoja), ]
rownames(datos) <- NULL

# Misma estructura de la plantilla (columnas y valores de la guía), con punto decimal en todo
write.csv(transform(datos, id_arbol = as.character(id_arbol),
                    exposicion = as.character(exposicion), fecha = format(fecha, "%d/%m/%Y")),
          SALIDA, row.names = FALSE, na = "", fileEncoding = "UTF-8")

3.2 Estadística descriptiva

resumen <- function(x) c(n = length(x), media = mean(x), mediana = median(x),
                         sd = sd(x), cv_pct = 100 * sd(x) / mean(x))

tabla_arbol <- do.call(rbind, lapply(split(datos, datos$id_arbol), function(d) {
  data.frame(id_arbol = as.character(d$id_arbol[1]),
             exposicion = as.character(d$exposicion[1]),
             t(resumen(d$area_cm2)))
}))
rownames(tabla_arbol) <- NULL

knitr::kable(tabla_arbol, digits = 2,
             col.names = c("Árbol", "Exposición", "n", "Media (cm²)", "Mediana (cm²)", "Desv. est. (cm²)", "CV (%)"),
             caption = "Área foliar por árbol (10 hojas por árbol)")
Área foliar por árbol (10 hojas por árbol)
Árbol Exposición n Media (cm²) Mediana (cm²) Desv. est. (cm²) CV (%)
G1_T1 sol 10 66.34 63.99 16.34 24.63
G1_T2 sol 10 60.78 61.00 5.65 9.29
G1_T3 sol 10 82.32 82.83 11.58 14.07
G1_T4 sombra 10 19.03 16.73 4.75 24.97
G1_T5 sombra 10 19.47 19.65 5.90 30.32
# Promedio por árbol: unidad de análisis para comparar sol y sombra
medias <- aggregate(area_cm2 ~ id_arbol + exposicion, data = datos, FUN = mean)

3.3 Gráficas

pch_arbol <- setNames(rep_len(c(15, 16, 17, 18, 8), nlevels(datos$id_arbol)), levels(datos$id_arbol))

op <- par(mar = c(4.2, 4.6, 3, 1), las = 1, bty = "l")
boxplot(area_cm2 ~ id_arbol, data = datos, boxwex = 0.6, border = "grey30",
        col = unname(col_exp[as.character(tabla_arbol$exposicion)]),
        xlab = "Árbol", ylab = expression("Área foliar (cm"^2*")"),
        main = "Área foliar por árbol", outline = FALSE)
stripchart(area_cm2 ~ id_arbol, data = datos, vertical = TRUE, method = "jitter",
           jitter = 0.12, pch = 16, col = rgb(0, 0, 0, 0.45), add = TRUE)
legend("topright", fill = col_exp, legend = c("Sol", "Sombra"), bty = "n")
Área foliar por árbol. Cada punto es una hoja.

Área foliar por árbol. Cada punto es una hoja.

par(op)
set.seed(1)
datos$x_exp <- as.integer(datos$exposicion)

op <- par(mar = c(4.2, 4.6, 3, 1), las = 1, bty = "l")
boxplot(area_cm2 ~ exposicion, data = datos, col = unname(col_exp), border = "grey30",
        outline = FALSE, names = c("Sol", "Sombra"), xlab = "Exposición",
        ylab = expression("Área foliar (cm"^2*")"), main = "Área foliar por exposición")
points(datos$x_exp + runif(nrow(datos), -0.18, 0.18), datos$area_cm2,
       pch = unname(pch_arbol[as.character(datos$id_arbol)]), col = rgb(0, 0, 0, 0.55))
points(as.integer(medias$exposicion), medias$area_cm2, pch = 23, bg = "red", cex = 1.8)
legend("topright", legend = c(levels(datos$id_arbol), "promedio del árbol"),
       pch = c(unname(pch_arbol), 23), pt.bg = c(rep(NA, length(pch_arbol)), "red"),
       bty = "n", cex = 0.8)
Área foliar por exposición. Puntos = hojas (símbolo según árbol); rombos rojos = promedio de cada árbol.

Área foliar por exposición. Puntos = hojas (símbolo según árbol); rombos rojos = promedio de cada árbol.

par(op)

3.4 Valores atípicos

Cada hoja se compara con las de su propio árbol, con dos criterios:

  • Regla de Tukey (1.5 × IQR): marca las hojas fuera de los bigotes del boxplot.
  • Puntaje z modificado (mediana y MAD): marca las hojas con |z| > 3.5; es más robusto porque los propios atípicos no lo inflan.

Una hoja marcada es solo candidata. Se eliminaría únicamente con evidencia de error de medición (trazo dudoso o foto defectuosa); si no, se conserva porque puede ser variación natural.

datos$z_arbol <- ave(datos$area_cm2, datos$id_arbol,
                     FUN = function(x) (x - mean(x)) / sd(x))
datos$z_mod <- ave(datos$area_cm2, datos$id_arbol, FUN = function(x) {
  mad_x <- mad(x, constant = 1)                      # mediana de las desviaciones absolutas
  if (mad_x == 0) return(rep(0, length(x)))
  0.6745 * (x - median(x)) / mad_x
})
datos$atipico_tukey <- ave(datos$area_cm2, datos$id_arbol,
                           FUN = function(x) as.numeric(x %in% boxplot.stats(x)$out)) == 1
datos$atipico_z <- abs(datos$z_mod) > 3.5

candidatas <- datos[datos$atipico_tukey | datos$atipico_z,
                    c("id_arbol", "n_hoja", "area_cm2", "z_arbol", "z_mod", "atipico_tukey", "atipico_z", "observaciones")]

if (nrow(candidatas) == 0) {
  cat("Ninguna hoja fue marcada por los dos criterios.\n")
} else {
  knitr::kable(candidatas, digits = 2, row.names = FALSE,
               col.names = c("Árbol", "Hoja", "Área (cm²)", "z (media, sd)", "z modificado",
                             "Marca Tukey", "Marca |z mod|>3.5", "Observaciones"),
               caption = "Hojas candidatas a atípicas")
}
Hojas candidatas a atípicas
Árbol Hoja Área (cm²) z (media, sd) z modificado Marca Tukey Marca |z mod|>3.5 Observaciones
G1_T2 1 52.18 -1.52 -2.76 TRUE FALSE NA
G1_T2 5 71.09 1.83 3.16 TRUE FALSE NA
G1_T2 10 51.64 -1.62 -2.93 TRUE FALSE NA
n_tukey <- sum(datos$atipico_tukey)
n_z     <- sum(datos$atipico_z)
n_obs   <- sum(!is.na(datos$observaciones))
max_zmod <- max(abs(datos$z_mod))

if (n_tukey > 0) {
  arbol_tukey <- unique(as.character(datos$id_arbol[datos$atipico_tukey]))
  cv_tukey    <- tabla_arbol$cv_pct[tabla_arbol$id_arbol %in% arbol_tukey]
  max_z_tukey <- max(abs(datos$z_arbol[datos$atipico_tukey]))
  txt_tukey <- sprintf(
    "La regla de Tukey marcó %d hoja(s), todas del árbol %s, que tiene el CV más bajo de la muestra (%.0f %%): con un núcleo tan compacto y solo 10 hojas, la regla se vuelve muy estricta y marca hojas a menos de %.1f desviaciones estándar de la media del árbol.",
    n_tukey, paste(arbol_tukey, collapse = ", "), min(cv_tukey), ceiling(max_z_tukey * 10) / 10)
} else {
  txt_tukey <- "La regla de Tukey no marcó ninguna hoja."
}

# Sensibilidad: repetir la comparación sin las hojas marcadas por Tukey
datos_sin  <- datos[!datos$atipico_tukey, ]
medias_sin <- aggregate(area_cm2 ~ id_arbol + exposicion, data = datos_sin, FUN = mean)
t_sin      <- t.test(area_cm2 ~ exposicion, data = medias_sin)
dif_sin    <- diff(rev(tapply(medias_sin$area_cm2, medias_sin$exposicion, mean)))

Decisión: se conservan las 50 hojas. La regla de Tukey marcó 3 hoja(s), todas del árbol G1_T2, que tiene el CV más bajo de la muestra (9 %): con un núcleo tan compacto y solo 10 hojas, la regla se vuelve muy estricta y marca hojas a menos de 1.9 desviaciones estándar de la media del árbol. El criterio robusto marcó 0 hoja(s) (máximo |z modificado| = 3.38) y la columna observaciones tiene 0 nota(s), es decir, no hay registro de errores de medición. Al repetir la comparación sin las hojas de Tukey, la diferencia sol − sombra pasa a 50.9 cm² (p de Welch = 0.014), así que la conclusión no depende de esas hojas.

3.5 Comparación sol vs sombra

Las 10 hojas de un árbol comparten genotipo, edad, suelo y microclima, por lo que no son réplicas independientes. La unidad de análisis es el promedio por árbol.

n_por_cat <- table(medias$exposicion)
n_sol <- as.integer(n_por_cat["sol"]); n_sombra <- as.integer(n_por_cat["sombra"])

knitr::kable(medias, digits = 2, row.names = FALSE,
             col.names = c("Árbol", "Exposición", "Área media (cm²)"),
             caption = "Promedio de área foliar por árbol (unidad de análisis)")
Promedio de área foliar por árbol (unidad de análisis)
Árbol Exposición Área media (cm²)
G1_T1 sol 66.34
G1_T2 sol 60.78
G1_T3 sol 82.32
G1_T4 sombra 19.03
G1_T5 sombra 19.47
m_sol    <- mean(medias$area_cm2[medias$exposicion == "sol"])
m_sombra <- mean(medias$area_cm2[medias$exposicion == "sombra"])
rng_sol    <- range(medias$area_cm2[medias$exposicion == "sol"])
rng_sombra <- range(medias$area_cm2[medias$exposicion == "sombra"])
sin_solape <- rng_sol[1] > rng_sombra[2]

t_arboles <- t.test(area_cm2 ~ exposicion, data = medias)   # Welch (varianzas desiguales)
w_arboles <- wilcox.test(area_cm2 ~ exposicion, data = medias, exact = TRUE)
ic <- t_arboles$conf.int

knitr::kable(
  data.frame(
    prueba = c("t de Welch", "Wilcoxon–Mann–Whitney exacta"),
    estadistico = c(sprintf("t = %.2f (gl = %.1f)", t_arboles$statistic, t_arboles$parameter),
                    sprintf("W = %.0f", w_arboles$statistic)),
    p = c(sprintf("%.3f", t_arboles$p.value), sprintf("%.2f", w_arboles$p.value))),
  col.names = c("Prueba (sobre promedios por árbol)", "Estadístico", "Valor p"),
  caption = "Comparación de sol vs sombra")
Comparación de sol vs sombra
Prueba (sobre promedios por árbol) Estadístico Valor p
t de Welch t = 7.83 (gl = 2.0) 0.016
Wilcoxon–Mann–Whitney exacta W = 6 0.20
# Pseudorreplicación: se calcula solo para mostrar por qué NO se deben usar las 50 hojas
t_hojas <- t.test(area_cm2 ~ exposicion, data = datos)
av  <- anova(lm(area_cm2 ~ id_arbol, data = datos))
msb <- av["id_arbol", "Mean Sq"]; msw <- av["Residuals", "Mean Sq"]
k   <- mean(table(datos$id_arbol))
icc <- (msb - msw) / (msb + (k - 1) * msw)    # correlación intraclase

¿Cuántos árboles hay realmente por categoría? 3 de sol y 2 de sombra: el tamaño de muestra efectivo es 5, no 50. Lo confirma la correlación intraclase (ICC = 0.89): el 89 % de la variación del área ocurre entre árboles. Tratar las 50 hojas como independientes daría p = 1.2e-19, un resultado inválido por pseudorreplicación.

El área media por árbol fue de 60.8 a 82.3 cm² en sol y de 19.0 a 19.5 cm² en sombra. Las hojas de sol fueron en promedio 3.6 veces más grandes (diferencia de 50.6 cm²; IC 95 %: 22.8 a 78.3 cm²). Los promedios por árbol de ambas categorías no se solapan. La prueba t de Welch da p = 0.016, pero con solo ≈2 grados de libertad y un intervalo muy ancho. La prueba exacta no depende de la normalidad y su p de 0.20 es el mínimo posible con 3 vs 2 árboles (solo existen 10 ordenamientos), de modo que no puede ser significativa aunque la separación sea total.

4 Discusión

4.1 Diferencias observadas y su relación con la hipótesis

Las hojas de sol fueron claramente más grandes que las de sombra, y esa diferencia supera con holgura la variación entre hojas de un mismo árbol (CV por árbol de 9 % a 30 %, sin patrón por exposición). El resultado coincide con la dirección de la hipótesis, pero no la confirma: con 3 y 2 árboles la evidencia es frágil y hay explicaciones alternativas (ver más abajo).

Dentro de una misma especie suele esperarse lo contrario (hojas de sombra más grandes y delgadas, hojas de sol más pequeñas y gruesas). Aquí se comparan árboles distintos, así que ese patrón no se puede evaluar con estos datos.

4.2 Factores ambientales posibles

  • Luz: más luz aporta más carbono para expandir la lámina, pero también más calor y pérdida de agua.
  • Agua: limita la expansión celular; el suelo pavimentado o compactado reduce la infiltración y el espacio para raíces.
  • Temperatura y humedad del aire: el déficit de presión de vapor es mayor al sol y condiciona el tamaño foliar.
  • Suelo y nutrientes: fertilidad, compactación y competencia con otros árboles o con el césped.

4.3 Qué más pudo influir

ids_sol <- as.character(medias$id_arbol[medias$exposicion == "sol"])
ids_som <- as.character(medias$id_arbol[medias$exposicion == "sombra"])
min_dentro <- function(ids) min(D[ids, ids][upper.tri(D[ids, ids])])
d_sol_min <- min_dentro(ids_sol)
d_som_min <- min_dentro(ids_som)
d_entre   <- min(D[ids_sol, ids_som])
  • Morfotipo o especie. No se identificó ningún árbol. Si los de sol y de sombra son especies distintas, la diferencia puede deberse a la especie y no a la luz; es la explicación alternativa más importante y no se puede descartar.
  • Sitio. Los árboles de sol más cercanos entre sí están a 26 m y los de sombra a 39 m, mientras que ningún árbol de sol está a menos de 333 m de uno de sombra. La exposición queda así mezclada con el lugar (suelo, riego, pavimento, manejo del césped).
  • Edad y tamaño del árbol. No se registraron y pueden afectar el tamaño foliar con el mismo ambiente.
  • Elección de las hojas. Fue un muestreo informal (sin sorteo con números aleatorios), repartido en distintas ramas y limitado a hojas desarrolladas, por lo que las conclusiones se refieren a ese tipo de hojas.
  • Error de medición. El trazo, el marco y la fotografía introducen incertidumbre en el área; es pequeña frente a una diferencia de 3.6 veces, pero sí afecta la comparación entre árboles parecidos.

4.4 Hipótesis y resultados

Resultado (lo respaldan los datos) Hipótesis (los datos no lo demuestran)
Los 3 árboles de sol tuvieron hojas más grandes que los 2 de sombra, en la dirección esperada. Que la causa sea la luz: la exposición está comprometida por la especie, el sitio y la edad del árbol.
La mayor parte de la variación está entre árboles, no entre hojas de un mismo árbol. Que el patrón se repita en otros árboles o especies del campus.
Con 3 y 2 árboles la evidencia estadística es débil (intervalo amplio, p exacto mínimo = 0.2). Cualquier mecanismo (agua, temperatura, suelo): requeriría medir esas variables.

5 Limitaciones

  • Poco poder estadístico: 5 árboles efectivos (3 sol, 2 sombra). Para conclusiones firmes harían falta más árboles por categoría, de la misma especie y en varios sitios, por ejemplo combinando los datos de todos los grupos de la clase.
  • Exposición cualitativa: la clasificación sol/sombra es binaria y no se midió ninguna variable ambiental (radiación, humedad del suelo, temperatura).
  • Una sola fecha y una medición por hoja: no se estimó el error de medición con réplicas, y el área puede variar con la edad de la hoja y la época.

6 Anexo: información de la sesión

sessionInfo()
## R version 4.5.2 (2025-10-31 ucrt)
## Platform: x86_64-w64-mingw32/x64
## Running under: Windows 10 x64 (build 19045)
## 
## Matrix products: default
##   LAPACK version 3.12.1
## 
## locale:
## [1] LC_COLLATE=Spanish_Colombia.utf8  LC_CTYPE=Spanish_Colombia.utf8   
## [3] LC_MONETARY=Spanish_Colombia.utf8 LC_NUMERIC=C                     
## [5] LC_TIME=Spanish_Colombia.utf8    
## 
## time zone: America/Bogota
## tzcode source: internal
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## loaded via a namespace (and not attached):
##  [1] digest_0.6.39     R6_2.6.1          fastmap_1.2.0     xfun_0.56        
##  [5] cachem_1.1.0      knitr_1.51        htmltools_0.5.9   rmarkdown_2.30   
##  [9] lifecycle_1.0.5   cli_3.6.5         sass_0.4.10       jquerylib_0.1.4  
## [13] compiler_4.5.2    rstudioapi_0.18.0 tools_4.5.2       evaluate_1.0.5   
## [17] bslib_0.10.0      yaml_2.3.12       otel_0.2.0        jsonlite_2.0.0   
## [21] rlang_1.1.7