1 Introducción

Este informe desarrolla los literales a–e del ejercicio 7 a partir del archivo suministrado Life Expectancy Data (1).csv. Se estudian la expectativa de vida (Life expectancy), la mortalidad adulta (Adult Mortality) y el índice de masa corporal (BMI). Se revisan los datos faltantes, se construyen diagramas de caja y densidad y se calculan medidas de tendencia central, posición, dispersión y forma.

Los registros corresponden a combinaciones de país y año, no a personas individuales. El análisis reúne los registros disponibles sin ponderación por población. En consecuencia, la media obtenida no debe interpretarse como un promedio mundial ponderado ni como evidencia de causalidad. Las unidades y definiciones de Adult Mortality y BMI deben contrastarse con el diccionario original: el archivo suministrado no incluye ese documento. Se conservan las escalas y los valores registrados, sin aplicar categorías clínicas.

2 Objetivos

Objetivo general: Analizar descriptivamente las tres variables solicitadas mediante medidas estadísticas y representaciones gráficas en R.

Objetivos específicos:

3 Preparación y criterios de cálculo

Guarde este archivo .Rmd y el CSV en una misma carpeta. Abra el .Rmd en RStudio y pulse Knit → Knit to HTML. Instale una sola vez, desde la consola, install.packages(c("rmarkdown", "knitr")) si no dispone de estos paquetes. Los cálculos y gráficos usan R base; no requieren dplyr, ggplot2, moments ni e1071.

archivo <- params$archivo
if (!file.exists(archivo)) {
  stop("No se encontró el CSV. Coloque 'Life Expectancy Data (1).csv' junto al Rmd.")
}
base <- read.csv(archivo, check.names = FALSE, stringsAsFactors = FALSE,
                 na.strings = c("", "NA", "N/A", "NULL", "null", "NaN"),
                 strip.white = TRUE)
# El CSV contiene espacios en algunos encabezados.
names(base) <- trimws(sub("^\ufeff", "", names(base)))
variables <- c("Life expectancy", "Adult Mortality", "BMI")
if (!all(variables %in% names(base))) stop("Faltan columnas requeridas en el CSV.")
seleccion <- base[, variables, drop = FALSE]
if (!all(vapply(seleccion, is.numeric, logical(1)))) {
  stop("Alguna variable no es numérica: revise el separador y los valores del CSV.")
}
etiquetas <- c("Expectativa de vida", "Mortalidad adulta", "BMI")

Se utilizan cuartiles y percentiles de quantile(type = 7), que es el método predeterminado de R. Se presentan varianza y desviación estándar muestrales (divisor \(n-1\)) y también sus versiones poblacionales (divisor \(n\)) para describir los registros como un conjunto completo. La asimetría y el exceso de curtosis se calculan con corrección muestral de Fisher–Pearson.

4 Literal a. Valores nulos y depuración

revision <- data.frame(
  Variable = etiquetas,
  Registros = nrow(seleccion),
  Nulos = colSums(is.na(seleccion)),
  Porcentaje_nulos = 100 * colMeans(is.na(seleccion)),
  row.names = NULL
)
knitr::kable(revision, digits = 3, caption = "Valores faltantes antes de la limpieza")
Valores faltantes antes de la limpieza
Variable Registros Nulos Porcentaje_nulos
Expectativa de vida 2938 10 0.340
Mortalidad adulta 2938 10 0.340
BMI 2938 34 1.157
# Criterio: casos completos únicamente en las tres variables del ejercicio.
completos <- complete.cases(seleccion)
datos <- seleccion[completos, , drop = FALSE]
n_eliminados <- sum(!completos)
stopifnot(!anyNA(datos), nrow(datos) > 3,
          all(vapply(datos, function(x) all(is.finite(x)), logical(1))))
knitr::kable(data.frame(
  Etapa = c("Base original", "Filas eliminadas", "Base analizada"),
  Registros = c(nrow(base), n_eliminados, nrow(datos))
))
Etapa Registros
Base original 2938
Filas eliminadas 42
Base analizada 2896

Resultado: de 2938 registros se eliminan 42 (1,43 %), quedando 2896 registros completos. La suma de faltantes por columna no equivale al número de filas eliminadas, porque una fila puede tener faltantes en varias variables. No se eliminan filas por faltantes en columnas ajenas al ejercicio. Este criterio permite comparar las tres variables sobre los mismos registros, aunque la eliminación puede introducir sesgo si los faltantes no son aleatorios.

5 Literales b y c. Boxplots, cuartiles y extremos

Se usa el criterio de Tukey: son atípicos los valores menores que \(Q_1-1{,}5\,RIC\) o mayores que \(Q_3+1{,}5\,RIC\), donde \(RIC=Q_3-Q_1\). No se eliminan los atípicos, porque el ejercicio solo pide eliminar valores nulos. Los bigotes llegan a los datos extremos que permanecen dentro de esos límites. El mínimo y el máximo reales se muestran con etiquetas independientes.

Para mantener coherencia exacta con las tablas, se dibujan las cajas con los cuartiles type = 7; las bisagras del boxplot() predeterminado pueden diferir ligeramente de estos cuartiles.

resumen_caja <- function(x) {
  q <- unname(quantile(x, c(.25, .5, .75), type = 7))
  ric <- q[3] - q[1]
  lim <- c(q[1] - 1.5 * ric, q[3] + 1.5 * ric)
  dentro <- x[x >= lim[1] & x <= lim[2]]
  c(Minimo = min(x), Q1 = q[1], Mediana = q[2], Q3 = q[3],
    Maximo = max(x), RIC = ric, Limite_inferior = lim[1],
    Limite_superior = lim[2], Bigote_inferior = min(dentro),
    Bigote_superior = max(dentro),
    Atipicos = sum(x < lim[1] | x > lim[2]))
}
cajas <- t(vapply(datos, resumen_caja, numeric(11)))
rownames(cajas) <- etiquetas
knitr::kable(cajas[, 1:6], digits = 3, caption = "Cuartiles y extremos reales")
Cuartiles y extremos reales
Minimo Q1 Mediana Q3 Maximo RIC
Expectativa de vida 36.3 63.475 72.20 75.7 89.0 12.225
Mortalidad adulta 1.0 73.000 142.50 225.0 723.0 152.000
BMI 1.0 19.300 43.35 56.1 77.6 36.800
knitr::kable(cajas[, 7:11], digits = 3, caption = "Límites, bigotes y número de atípicos")
Límites, bigotes y número de atípicos
Limite_inferior Limite_superior Bigote_inferior Bigote_superior Atipicos
Expectativa de vida 45.138 94.038 45.3 89.0 20
Mortalidad adulta -155.000 453.000 1.0 453.0 87
BMI -35.900 111.300 1.0 77.6 0
boxplot_anotado <- function(x, titulo, color = "#8ECAC3") {
  a <- resumen_caja(x)
  atipicos <- x[x < a["Limite_inferior"] | x > a["Limite_superior"]]
  objeto <- list(stats = matrix(a[c("Bigote_inferior", "Q1", "Mediana",
                                    "Q3", "Bigote_superior")], ncol = 1),
                 n = length(x), conf = matrix(c(NA_real_, NA_real_), ncol = 1),
                 out = atipicos, group = rep(1, length(atipicos)), names = "")
  anterior <- par(mar = c(5, 3, 4, 2))
  on.exit(par(anterior))
  amplitud <- diff(range(x))
  bxp(objeto, horizontal = TRUE, boxfill = color, outline = TRUE,
      main = titulo, xlab = "Valor registrado", xlim = c(.25, 1.85),
      ylim = range(x) + c(-.1, .1) * amplitud, show.names = FALSE,
      outpch = 1, outcex = .65)
  valores <- a[c("Minimo", "Q1", "Mediana", "Q3", "Maximo")]
  alturas <- c(1.55, .55, 1.55, .55, 1.55)
  nombres <- c("Mínimo", "Q1", "Q2 / Mediana", "Q3", "Máximo")
  for (j in seq_along(valores)) {
    segments(valores[j], 1, valores[j], alturas[j], col = "#64748B", lty = 3)
    text(valores[j], alturas[j], paste0(nombres[j], "\n", fmt(valores[j])),
         cex = .78, pos = if (alturas[j] > 1) 3 else 1)
  }
}
for (i in seq_along(datos)) {
  cat("\n\n### ", etiquetas[i], "\n\n", sep = "")
  boxplot_anotado(datos[[i]], etiquetas[i])
  cat("\n\nEl 50 % central se encuentra entre **", fmt(cajas[i, "Q1"]),
      "** y **", fmt(cajas[i, "Q3"]), "**. La mediana es **",
      fmt(cajas[i, "Mediana"]), "** y se identifican **",
      cajas[i, "Atipicos"], "** observaciones atípicas según el criterio adoptado.\n\n", sep = "")
}

5.0.1 Expectativa de vida

El 50 % central se encuentra entre 63,475 y 75,700. La mediana es 72,200 y se identifican 20 observaciones atípicas según el criterio adoptado.

5.0.2 Mortalidad adulta

El 50 % central se encuentra entre 73,000 y 225,000. La mediana es 142,500 y se identifican 87 observaciones atípicas según el criterio adoptado.

5.0.3 BMI

El 50 % central se encuentra entre 19,300 y 56,100. La mediana es 43,350 y se identifican 0 observaciones atípicas según el criterio adoptado.

6 Literal d. Tendencia central, posición y dispersión

6.1 Tendencia central

La media resume el promedio aritmético; la mediana divide los datos ordenados en dos mitades y la moda identifica el valor registrado con mayor frecuencia. En variables continuas, la moda depende del redondeo y de la precisión del archivo; no necesariamente coincide con el pico de la densidad.

moda <- function(x) {
  valores <- sort(unique(x))
  frecuencias <- tabulate(match(x, valores))
  if (max(frecuencias) == 1) return(NA_real_)
  valores[frecuencias == max(frecuencias)]
}
central <- data.frame(
  Variable = etiquetas,
  Media = vapply(datos, mean, numeric(1)),
  Mediana = vapply(datos, median, numeric(1)),
  Moda = vapply(datos, function(x) paste(fmt(moda(x)), collapse = "; "), character(1)),
  Frecuencia_modal = vapply(datos, function(x) max(table(x)), integer(1)),
  row.names = NULL
)
knitr::kable(central, digits = 3, caption = "Medidas de tendencia central")
Medidas de tendencia central
Variable Media Mediana Moda Frecuencia_modal
Expectativa de vida 69.351 72.20 73,000 45
Mortalidad adulta 163.376 142.50 12,000 34
BMI 38.235 43.35 58,500 18

6.2 Posición

Se muestran los cuartiles, los nueve deciles y los percentiles del 1 al 99. Por ejemplo, el percentil 25 equivale a Q1, el percentil 50 a la mediana y el percentil 75 a Q3. Cada percentil representa un punto de corte aproximado; los empates e interpolaciones afectan la proporción exacta de observaciones por debajo de él.

cuartiles <- t(vapply(datos, function(x) quantile(x, c(.25, .5, .75), type = 7), numeric(3)))
rownames(cuartiles) <- etiquetas
knitr::kable(cuartiles, digits = 3, caption = "Cuartiles")
Cuartiles
25% 50% 75%
Expectativa de vida 63.475 72.20 75.7
Mortalidad adulta 73.000 142.50 225.0
BMI 19.300 43.35 56.1
deciles <- data.frame(Decil = paste0("D", 1:9),
                     sapply(datos, quantile, probs = (1:9)/10, type = 7),
                     check.names = FALSE)
knitr::kable(deciles, digits = 3, row.names = FALSE, caption = "Deciles")
Deciles
Decil Life expectancy Adult Mortality BMI
D1 54.90 19.0 11.90
D2 61.10 62.0 17.50
D3 65.55 83.0 22.50
D4 68.90 121.0 29.40
D5 72.20 142.5 43.35
D6 73.60 167.0 49.40
D7 74.90 197.0 54.70
D8 77.00 254.0 57.60
D9 79.70 334.0 61.70
percentiles <- data.frame(Percentil = paste0("P", 1:99),
                         sapply(datos, quantile, probs = (1:99)/100, type = 7),
                         check.names = FALSE)
knitr::kable(percentiles[c(5, 10, 25, 50, 75, 90, 95), ], digits = 3,
             row.names = FALSE, caption = "Percentiles seleccionados; tabla completa en el anexo")
Percentiles seleccionados; tabla completa en el anexo
Percentil Life expectancy Adult Mortality BMI
P5 51.400 12.75 5.200
P10 54.900 19.00 11.900
P25 63.475 73.00 19.300
P50 72.200 142.50 43.350
P75 75.700 225.00 56.100
P90 79.700 334.00 61.700
P95 82.000 411.00 64.525

6.3 Dispersión

El rango es máximo menos mínimo; el RIC resume la amplitud del 50 % central. La varianza se expresa en unidades al cuadrado y la desviación estándar en las unidades originales. El coeficiente de variación se calcula como \(100s/\bar{x}\); su comparación requiere escalas de razón y una media positiva. La desviación absoluta mediana (MAD) se presenta sin factor de normalización (constant = 1).

dispersion <- function(x) {
  c(Rango = diff(range(x)), RIC = IQR(x, type = 7),
    Varianza_muestral = var(x), DE_muestral = sd(x),
    Varianza_poblacional = mean((x - mean(x))^2),
    DE_poblacional = sqrt(mean((x - mean(x))^2)),
    CV_porcentaje = 100 * sd(x)/mean(x),
    Desviacion_media_absoluta = mean(abs(x - mean(x))),
    MAD_sin_ajuste = mad(x, constant = 1))
}
dispers <- t(vapply(datos, dispersion, numeric(9)))
rownames(dispers) <- etiquetas
knitr::kable(t(dispers), digits = 3, caption = "Medidas de dispersión")
Medidas de dispersión
Expectativa de vida Mortalidad adulta BMI
Rango 52.700 722.000 76.600
RIC 12.225 152.000 36.800
Varianza_muestral 90.021 15347.991 398.385
DE_muestral 9.488 123.887 19.960
Varianza_poblacional 89.990 15342.691 398.248
DE_poblacional 9.486 123.866 19.956
CV_porcentaje 13.681 75.829 52.202
Desviacion_media_absoluta 7.731 94.569 18.032
MAD_sin_ajuste 5.600 74.500 16.350

Interpretación: la expectativa de vida tiene una media de 69,351 años y una desviación estándar muestral de 9,488 años. La mortalidad adulta presenta un CV de 75,829 %, frente a 13,681 % en expectativa de vida y 52,202 % en BMI. Bajo las condiciones de interpretación del CV, la mortalidad adulta es la variable con mayor dispersión relativa en estos registros. No debe compararse la dispersión absoluta entre variables con unidades diferentes.

7 Literal e. Asimetría, curtosis y densidad

7.1 Definiciones y clasificación

Se utiliza el coeficiente de asimetría corregido \(G_1\). Un resultado negativo indica asimetría hacia la izquierda y uno positivo hacia la derecha. Un valor próximo a cero indica poca asimetría según esta medida; por sí solo no prueba simetría ni normalidad.

Se utiliza el exceso de curtosis corregido \(G_2\): positivo se clasifica como leptocúrtico; negativo, platicúrtico; cero, mesocúrtico. También se informa \(G_2+3\) para expresar la curtosis en la escala con referencia normal igual a 3. La clasificación es descriptiva: no constituye una prueba estadística. La curtosis refleja especialmente la contribución de las colas y valores extremos; no debe interpretarse solo como la altura del pico.

Sea \(m_r=n^{-1}\sum (x_i-\bar{x})^r\):

\[ G_1=\frac{\sqrt{n(n-1)}}{n-2}\frac{m_3}{m_2^{3/2}}, \qquad G_2=\frac{n-1}{(n-2)(n-3)}\left[(n+1)\left(\frac{m_4}{m_2^2}-3\right)+6\right]. \]

forma <- function(x) {
  n <- length(x)
  z <- x - mean(x)
  m2 <- mean(z^2)
  m3 <- mean(z^3)
  m4 <- mean(z^4)
  G1 <- sqrt(n * (n - 1))/(n - 2) * m3/m2^(3/2)
  G2 <- (n - 1)/((n - 2) * (n - 3)) * ((n + 1) * (m4/m2^2 - 3) + 6)
  c(Asimetria = G1, Exceso_curtosis = G2, Curtosis = G2 + 3)
}
formas <- t(vapply(datos, forma, numeric(3)))
rownames(formas) <- etiquetas
clasificacion <- data.frame(
  Variable = etiquetas, formas,
  Direccion = ifelse(formas[, 1] < 0, "Negativa (izquierda)",
                    ifelse(formas[, 1] > 0, "Positiva (derecha)", "Nula")),
  Tipo_curtosis = ifelse(formas[, 2] < 0, "Platicúrtica",
                        ifelse(formas[, 2] > 0, "Leptocúrtica", "Mesocúrtica")),
  row.names = NULL
)
knitr::kable(clasificacion, digits = 4, caption = "Medidas de forma y clasificación")
Medidas de forma y clasificación
Variable Asimetria Exceso_curtosis Curtosis Direccion Tipo_curtosis
Expectativa de vida -0.6640 -0.1750 2.8250 Negativa (izquierda) Platicúrtica
Mortalidad adulta 1.2090 1.8782 4.8782 Positiva (derecha) Leptocúrtica
BMI -0.2306 -1.3130 1.6870 Negativa (izquierda) Platicúrtica
  • Expectativa de vida: asimetría negativa (-0,6640), con mayor extensión hacia los valores bajos; su media es menor que su mediana. El exceso de curtosis de -0,1750 determina la clasificación platicúrtica, aunque se encuentra próximo a cero.
  • Mortalidad adulta: asimetría positiva (1,2090), con cola hacia los valores altos. Su exceso de curtosis de 1,8782 corresponde a una distribución leptocúrtica.
  • BMI: asimetría negativa de menor magnitud (-0,2306) y exceso de curtosis de -1,3130, correspondiente a una distribución platicúrtica. Una asimetría pequeña no descarta varios picos en la distribución.

7.2 Gráficos de densidad con la media

Las curvas son estimaciones de densidad por núcleo gaussiano con ancho de banda nrd0. El eje vertical representa densidad, no frecuencias ni probabilidades puntuales. La línea roja discontinua localiza la media; la azul punteada permite compararla con la mediana. El suavizado puede extender la curva fuera del intervalo observado y no implica que existan registros allí.

for (i in seq_along(datos)) {
  x <- datos[[i]]
  den <- density(x, kernel = "gaussian", bw = "nrd0")
  plot(den, main = paste("Densidad:", etiquetas[i]),
       xlab = "Valor registrado", ylab = "Densidad",
       ylim = c(0, max(den$y) * 1.3), lwd = 2, col = "#147D92")
  polygon(c(den$x, rev(den$x)), c(den$y, rep(0, length(den$y))),
          col = adjustcolor("#8ECAC3", alpha.f = .5), border = NA)
  lines(den, col = "#147D92", lwd = 2)
  abline(v = mean(x), col = "#C0392B", lty = 2, lwd = 2)
  abline(v = median(x), col = "#3157A4", lty = 3, lwd = 2)
  legend("topright", legend = c(paste("Media =", fmt(mean(x))),
                               paste("Mediana =", fmt(median(x)))),
         col = c("#C0392B", "#3157A4"), lty = c(2, 3), lwd = 2,
         bty = "n", cex = .85)
}

8 Conclusiones

  1. Se identificaron faltantes en las tres variables y se conservaron 2896 casos completos. Las estadísticas de todo el informe utilizan esa misma base depurada.
  2. La expectativa de vida se concentra alrededor de una mediana de 72,200 años; su asimetría negativa muestra una mayor extensión hacia los valores inferiores.
  3. La mortalidad adulta combina asimetría positiva, exceso de curtosis positivo y la mayor dispersión relativa de las tres variables.
  4. BMI presenta asimetría negativa de menor magnitud y exceso de curtosis negativo. Sus valores se interpretan en la escala registrada, sin atribuirles categorías clínicas individuales.
  5. Los atípicos se mantuvieron. El archivo reúne distintos países y años, por lo que estas descripciones agregadas no explican tendencias temporales, diferencias entre países ni relaciones causales.

9 Anexo. Percentiles completos

knitr::kable(percentiles, digits = 3, row.names = FALSE,
             caption = "Percentiles 1 a 99; método type = 7")
Percentiles 1 a 99; método type = 7
Percentil Life expectancy Adult Mortality BMI
P1 45.600 4.00 2.300
P2 47.100 7.00 2.700
P3 48.400 11.00 3.300
P4 49.800 12.00 4.380
P5 51.400 12.75 5.200
P6 52.000 14.00 5.700
P7 52.600 15.00 6.100
P8 53.300 16.00 6.500
P9 54.100 17.00 6.855
P10 54.900 19.00 11.900
P11 55.400 22.00 13.000
P12 56.200 23.00 13.740
P13 56.800 26.00 14.300
P14 57.400 29.00 14.830
P15 57.900 34.25 15.400
P16 58.300 39.40 15.900
P17 58.800 47.15 16.400
P18 59.300 54.10 16.700
P19 59.700 58.00 17.105
P20 61.100 62.00 17.500
P21 61.700 65.00 17.900
P22 62.100 66.00 18.200
P23 62.600 68.00 18.500
P24 63.000 71.00 18.900
P25 63.475 73.00 19.300
P26 64.000 75.00 19.700
P27 64.400 77.00 21.200
P28 64.800 79.00 21.600
P29 65.100 82.00 22.055
P30 65.550 83.00 22.500
P31 65.900 85.00 22.900
P32 66.240 88.00 23.500
P33 66.700 91.00 24.100
P34 67.100 93.00 24.800
P35 67.400 96.00 25.525
P36 67.700 98.00 26.200
P37 68.000 112.00 27.015
P38 68.210 115.00 27.810
P39 68.500 118.00 28.600
P40 68.900 121.00 29.400
P41 69.100 123.00 31.300
P42 69.400 125.00 32.300
P43 69.700 127.00 33.500
P44 70.000 129.00 35.200
P45 71.100 132.00 36.800
P46 71.400 134.00 38.200
P47 71.600 136.00 39.465
P48 71.800 138.00 41.400
P49 72.000 139.00 42.555
P50 72.200 142.50 43.350
P51 72.400 144.00 44.200
P52 72.600 146.00 44.900
P53 72.700 148.00 45.500
P54 72.800 152.30 46.100
P55 73.000 155.00 46.700
P56 73.000 158.00 47.200
P57 73.200 161.00 47.800
P58 73.300 163.00 48.310
P59 73.400 165.00 48.900
P60 73.600 167.00 49.400
P61 73.700 171.00 51.095
P62 73.800 174.00 51.490
P63 73.900 176.00 51.985
P64 74.100 178.00 52.400
P65 74.200 183.00 52.800
P66 74.400 185.00 53.300
P67 74.500 187.00 53.600
P68 74.600 189.00 54.100
P69 74.755 193.55 54.355
P70 74.900 197.00 54.700
P71 75.000 211.00 55.000
P72 75.200 216.00 55.200
P73 75.400 218.00 55.535
P74 75.500 222.00 55.800
P75 75.700 225.00 56.100
P76 76.000 228.20 56.400
P77 76.100 234.15 56.700
P78 76.400 241.00 57.000
P79 76.700 248.00 57.300
P80 77.000 254.00 57.600
P81 77.200 261.00 57.900
P82 77.600 267.00 58.200
P83 78.000 272.00 58.500
P84 78.100 277.00 58.700
P85 78.400 282.00 59.000
P86 78.700 287.00 59.300
P87 78.965 293.65 59.665
P88 79.160 299.00 59.900
P89 79.400 322.00 61.300
P90 79.700 334.00 61.700
P91 80.000 339.00 62.145
P92 81.000 352.80 62.600
P93 81.300 366.35 63.200
P94 81.600 382.00 63.800
P95 82.000 411.00 64.525
P96 82.300 429.00 65.320
P97 83.000 453.15 66.215
P98 84.000 495.10 67.610
P99 87.000 586.05 69.785

10 Fuentes y reproducibilidad

sessionInfo()
## R version 4.5.2 (2025-10-31 ucrt)
## Platform: x86_64-w64-mingw32/x64
## Running under: Windows 11 x64 (build 26200)
## 
## 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