1 Propósito de la aplicación

En esta práctica trabajaremos como si estuviéramos desarrollando una pequeña investigación biológica. El objetivo no es aprender comandos aislados, sino seguir un flujo de análisis:

pregunta biológica → datos → descripción → visualización → interpretación → inferencia.

La base contiene información simulada con fines didácticos sobre 60 puntos de muestreo de una ciénaga. En cada punto se registraron el sector, el hábitat, la concentración de clorofila-a, la temperatura, la turbidez y la profundidad.

La pregunta general es:

¿Cómo se comporta la concentración de clorofila-a en la ciénaga y qué podemos concluir, a partir de la muestra, sobre su valor medio en la población?

Antes de cualquier inferencia debemos conocer bien los datos.


2 1. Preparar RStudio e importar la base

2.1 1.1. Coloque los dos archivos en la misma carpeta

Para evitar problemas con rutas, guarde en una misma carpeta:

  • Codigos_Estudiante_Clorofila.R
  • clorofila_cienaga_v3.csv

Abra el archivo .Rmd desde RStudio y ejecute el siguiente código.

2.2 1.2. Cargar paquetes

Si es la primera vez que usa estos paquetes, instálelos una sola vez con:

install.packages("ggplot2")
install.packages("dplyr")

En cada nueva sesión de R solo necesita cargarlos:

library(ggplot2)
library(dplyr)

2.3 1.3. Importar los datos

# El archivo CSV debe estar en la misma carpeta que este documento.
datos <- read.csv("clorofila_cienaga_v3.csv")

Eso es todo lo que necesitamos para importar la base.

2.4 1.4. Verificar que la base fue leída correctamente

head(datos)
##   punto sector  habitat clorofila temperatura turbidez profundidad
## 1     1  Norte  Litoral     13.53        27.0      9.5         0.7
## 2     2  Norte  Litoral     14.33        27.6      8.6         1.6
## 3     3  Norte  Litoral     12.69        28.2      9.4         1.7
## 4     4  Norte  Litoral     13.47        27.7      8.7         1.3
## 5     5  Norte Pelágico     13.74        26.9     10.0         3.3
## 6     6  Norte Pelágico     12.84        27.9     10.5         3.0
dim(datos)
## [1] 60  7
str(datos)
## 'data.frame':    60 obs. of  7 variables:
##  $ punto      : int  1 2 3 4 5 6 7 8 9 10 ...
##  $ sector     : chr  "Norte" "Norte" "Norte" "Norte" ...
##  $ habitat    : chr  "Litoral" "Litoral" "Litoral" "Litoral" ...
##  $ clorofila  : num  13.5 14.3 12.7 13.5 13.7 ...
##  $ temperatura: num  27 27.6 28.2 27.7 26.9 27.9 28.2 27.1 27.5 26.4 ...
##  $ turbidez   : num  9.5 8.6 9.4 8.7 10 10.5 14.3 11.5 10.5 9.3 ...
##  $ profundidad: num  0.7 1.6 1.7 1.3 3.3 3 1.2 1.2 1.8 2.3 ...

Interpretación. Cada fila corresponde a un punto de muestreo. La base tiene 60 observaciones y siete variables. Las variables sector y habitat son cualitativas; clorofila, temperatura, turbidez y profundidad son cuantitativas.

Para que R trate correctamente las variables cualitativas, las convertimos en factores:

datos$sector  <- factor(datos$sector)
datos$habitat <- factor(datos$habitat)

3 2. Preguntas de investigación que responderemos

Durante el análisis intentaremos responder progresivamente:

  1. ¿Cómo se distribuyeron los puntos de muestreo entre sectores y hábitats?
  2. ¿Cuál es la concentración típica de clorofila-a?
  3. ¿Qué tan variables son las concentraciones?
  4. ¿La distribución es aproximadamente simétrica o presenta valores extremos?
  5. ¿Se observan diferencias descriptivas entre sectores o hábitats?
  6. ¿Existe alguna relación visible entre clorofila-a y turbidez?
  7. Finalmente, ¿qué podemos inferir sobre la media poblacional de clorofila-a?

Las primeras seis preguntas son principalmente descriptivas. La última requiere inferencia estadística.


4 3. Análisis descriptivo de variables cualitativas

Las variables cualitativas se describen principalmente mediante frecuencias absolutas, frecuencias relativas y diagramas de barras.

4.1 3.1. Sector de muestreo

table(datos$sector)
## 
## Centro  Norte    Sur 
##     20     20     20
round(100 * prop.table(table(datos$sector)), 1)
## 
## Centro  Norte    Sur 
##   33.3   33.3   33.3

4.1.1 Diagrama de barras

ggplot(datos, aes(x = sector)) +
  geom_bar(width = 0.65) +
  labs(
    title = "Puntos de muestreo por sector",
    x = "Sector",
    y = "Frecuencia"
  ) +
  theme_minimal(base_size = 13)

Interpretación. Los tres sectores aportan el mismo número de observaciones. Esto es útil porque evita que una diferencia descriptiva entre sectores sea simplemente consecuencia de tamaños muestrales muy distintos.

4.2 3.2. Tipo de hábitat

table(datos$habitat)
## 
##  Litoral Pelágico 
##       34       26
round(100 * prop.table(table(datos$habitat)), 1)
## 
##  Litoral Pelágico 
##     56.7     43.3
ggplot(datos, aes(x = habitat)) +
  geom_bar(width = 0.65) +
  labs(
    title = "Puntos de muestreo según hábitat",
    x = "Hábitat",
    y = "Frecuencia"
  ) +
  theme_minimal(base_size = 13)

Interpretación. La muestra incluye puntos litorales y pelágicos. El diagrama de barras permite comparar rápidamente la representación de ambas categorías.

4.3 3.3. Dos variables cualitativas simultáneamente

Ahora observamos cómo se distribuye el hábitat dentro de cada sector.

table(datos$sector, datos$habitat)
##         
##          Litoral Pelágico
##   Centro      10       10
##   Norte       12        8
##   Sur         12        8
ggplot(datos, aes(x = sector, fill = habitat)) +
  geom_bar(position = "fill") +
  scale_y_continuous(
    labels = function(x) paste0(round(100 * x), "%")
  ) +
  labs(
    title = "Composición del hábitat dentro de cada sector",
    x = "Sector",
    y = "Porcentaje",
    fill = "Hábitat"
  ) +
  theme_minimal(base_size = 13)

Interpretación. Este gráfico ya no muestra el número total de puntos, sino la composición porcentual de cada sector. Es especialmente útil cuando queremos comparar una variable cualitativa entre grupos.

Para variables cualitativas, los diagramas de barras suelen ser más fáciles de comparar que los diagramas circulares.


5 4. Análisis descriptivo de una variable cuantitativa

La variable principal del estudio es clorofila, medida en mg/m³.

x <- datos$clorofila

5.1 4.1. Resumen básico

summary(x)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##    9.43   13.09   14.59   14.49   15.73   20.72

La salida anterior proporciona mínimo, cuartiles, mediana, media y máximo. Complementamos con otras medidas.

n       <- length(x)
media   <- mean(x)
mediana <- median(x)
varianza <- var(x)
desv     <- sd(x)
rango    <- diff(range(x))
Q1       <- quantile(x, 0.25)
Q3       <- quantile(x, 0.75)
RIC      <- IQR(x)
CV       <- 100 * desv / media

resumen <- data.frame(
  Medida = c("n", "Media", "Mediana", "Varianza", "Desviación estándar",
             "Mínimo", "Q1", "Q3", "Máximo", "RIC", "CV (%)"),
  Valor = c(n, media, mediana, varianza, desv,
            min(x), Q1, Q3, max(x), RIC, CV)
)

knitr::kable(resumen, digits = 2)
Medida Valor
n 60.00
Media 14.49
Mediana 14.59
Varianza 5.08
Desviación estándar 2.25
Mínimo 9.43
Q1 13.09
Q3 15.73
Máximo 20.72
RIC 2.64
CV (%) 15.55

Lectura integrada de los resultados. La media y la mediana son muy próximas, lo que sugiere que el centro de la distribución no está fuertemente desplazado por una cola extrema. La desviación estándar describe la dispersión alrededor de la media, mientras que el RIC resume la amplitud del 50 % central de las observaciones. El CV expresa la dispersión en términos relativos respecto a la media.

5.2 4.2. Histograma

ggplot(datos, aes(x = clorofila)) +
  geom_histogram(
    bins = 10,
    color = "white"
  ) +
  geom_vline(
    xintercept = mean(datos$clorofila),
    linetype = 2,
    linewidth = 0.9
  ) +
  labs(
    title = "Distribución de la clorofila-a",
    subtitle = "La línea punteada representa la media muestral",
    x = "Clorofila-a (mg/m³)",
    y = "Frecuencia"
  ) +
  theme_minimal(base_size = 13)

Qué debemos observar. El histograma permite estudiar la forma global de la distribución: concentración de observaciones, posibles colas, huecos, grupos o valores poco frecuentes.

5.3 4.3. Curva de densidad

ggplot(datos, aes(x = clorofila)) +
  geom_density(linewidth = 1.1) +
  geom_vline(
    xintercept = mean(datos$clorofila),
    linetype = 2
  ) +
  geom_vline(
    xintercept = median(datos$clorofila),
    linetype = 3
  ) +
  labs(
    title = "Densidad de la clorofila-a",
    subtitle = "Líneas verticales: media y mediana",
    x = "Clorofila-a (mg/m³)",
    y = "Densidad"
  ) +
  theme_minimal(base_size = 13)

Interpretación. La curva de densidad es una versión suavizada de la distribución. No muestra frecuencias absolutas; ayuda a observar con mayor claridad la forma general.

5.4 4.4. Diagrama de caja

ggplot(datos, aes(x = "Muestra", y = clorofila)) +
  geom_boxplot(width = 0.25, outlier.shape = NA) +
  geom_jitter(
    width = 0.08,
    alpha = 0.55
  ) +
  labs(
    title = "Boxplot y observaciones individuales",
    y = "Clorofila-a (mg/m³)",
    x = NULL
  ) +
  theme_minimal(base_size = 13)

Interpretación. El boxplot resume mediana, cuartiles y posibles observaciones atípicas. Al superponer los puntos individuales evitamos ocultar la estructura real de la muestra.

5.5 4.5. Gráfico de violín con puntos individuales

ggplot(datos, aes(x = "Muestra", y = clorofila)) +
  geom_violin(trim = FALSE) +
  geom_boxplot(width = 0.12, outlier.shape = NA) +
  geom_jitter(width = 0.06, alpha = 0.45) +
  labs(
    title = "Distribución completa de la clorofila-a",
    x = NULL,
    y = "Clorofila-a (mg/m³)"
  ) +
  theme_minimal(base_size = 13)

El violín combina información de densidad con la lógica del boxplot y permite observar dónde se concentran más datos.


6 5. Descripción de la clorofila-a por grupos

Hasta ahora analizamos todos los puntos conjuntamente. Pero el contexto biológico sugiere preguntar si los sectores o hábitats presentan patrones diferentes.

6.1 5.1. Resumen por sector

resumen_sector <- datos %>%
  group_by(sector) %>%
  summarise(
    n = n(),
    media = mean(clorofila),
    mediana = median(clorofila),
    DE = sd(clorofila),
    Q1 = quantile(clorofila, 0.25),
    Q3 = quantile(clorofila, 0.75),
    CV = 100 * DE / media
  )

knitr::kable(resumen_sector, digits = 2)
sector n media mediana DE Q1 Q3 CV
Centro 20 15.58 15.80 2.39 14.63 16.68 15.31
Norte 20 13.37 13.74 1.56 12.67 14.41 11.64
Sur 20 14.52 14.58 2.25 12.92 15.56 15.50

6.2 5.2. Boxplot por sector

ggplot(datos, aes(x = sector, y = clorofila)) +
  geom_boxplot(outlier.shape = NA) +
  geom_jitter(width = 0.10, alpha = 0.60) +
  labs(
    title = "Clorofila-a por sector",
    subtitle = "Se muestran el resumen del boxplot y los datos individuales",
    x = "Sector",
    y = "Clorofila-a (mg/m³)"
  ) +
  theme_minimal(base_size = 13)

Interpretación. Descriptivamente, el sector Centro presenta una concentración media mayor que Norte y Sur. Sin embargo, los grupos se solapan y todavía no hemos realizado una comparación inferencial. Una diferencia entre medias muestrales no implica automáticamente una diferencia poblacional.

6.3 5.3. Histogramas separados por sector

ggplot(datos, aes(x = clorofila)) +
  geom_histogram(
    bins = 7,
    color = "white"
  ) +
  facet_wrap(~ sector) +
  labs(
    title = "Distribución de la clorofila-a en cada sector",
    x = "Clorofila-a (mg/m³)",
    y = "Frecuencia"
  ) +
  theme_minimal(base_size = 12)

Estos paneles permiten comparar no solo las medias, sino también la dispersión y la forma de las distribuciones.

6.4 5.4. Comparación por hábitat

resumen_habitat <- datos %>%
  group_by(habitat) %>%
  summarise(
    n = n(),
    media = mean(clorofila),
    mediana = median(clorofila),
    DE = sd(clorofila),
    CV = 100 * DE / media
  )

knitr::kable(resumen_habitat, digits = 2)
habitat n media mediana DE CV
Litoral 34 14.96 15.02 2.42 16.15
Pelágico 26 13.88 14.23 1.89 13.64
ggplot(datos, aes(x = habitat, y = clorofila)) +
  geom_violin(trim = FALSE) +
  geom_boxplot(width = 0.15, outlier.shape = NA) +
  geom_jitter(width = 0.08, alpha = 0.55) +
  labs(
    title = "Clorofila-a según hábitat",
    x = "Hábitat",
    y = "Clorofila-a (mg/m³)"
  ) +
  theme_minimal(base_size = 13)


7 6. Exploración descriptiva entre dos variables cuantitativas

Una pregunta adicional es si los puntos con mayor turbidez tienden a mostrar también mayores concentraciones de clorofila-a.

ggplot(datos, aes(x = turbidez, y = clorofila)) +
  geom_point(size = 2.4, alpha = 0.75) +
  labs(
    title = "Clorofila-a y turbidez",
    x = "Turbidez",
    y = "Clorofila-a (mg/m³)"
  ) +
  theme_minimal(base_size = 13)

Interpretación. El diagrama de dispersión permite explorar dirección, forma y fuerza aparente de una relación. En esta etapa no buscamos demostrar causalidad ni ajustar todavía un modelo de regresión.


8 7. Del análisis descriptivo a la inferencia

Llegados a este punto sabemos bastante sobre la muestra:

  • conocemos su tamaño;
  • conocemos la media y mediana observadas;
  • hemos cuantificado la variabilidad;
  • hemos inspeccionado la forma de la distribución;
  • hemos comparado descriptivamente sectores y hábitats;
  • hemos observado las mediciones individuales.

Pero todavía hay una pregunta que el análisis descriptivo no puede responder por sí solo:

¿Qué tan cerca está la media de esta muestra de la verdadera media de clorofila-a de la población de puntos de la ciénaga?

La media que calculamos es:

mean(datos$clorofila)
## [1] 14.492

Ese número describe nuestra muestra. Si volviéramos a seleccionar otros 60 puntos, la media cambiaría ligeramente. A esta variación entre muestras la llamamos variabilidad muestral.

Por eso necesitamos pasar de:

describir lo observado

hacia:

cuantificar la incertidumbre al generalizar desde la muestra hacia la población.

Ese es el origen natural de la inferencia estadística.


9 8. Error estándar e intervalo de confianza

Para la media, una medida de incertidumbre es el error estándar:

\[ SE(\bar X)=\frac{s}{\sqrt{n}}. \]

n <- nrow(datos)
media <- mean(datos$clorofila)
s <- sd(datos$clorofila)
SE <- s / sqrt(n)

SE
## [1] 0.29085

El intervalo de confianza del 95 % puede calcularse como:

\[ \bar x \pm t_{0.975,n-1}\frac{s}{\sqrt n}. \]

tcritico <- qt(0.975, df = n - 1)
LI <- media - tcritico * SE
LS <- media + tcritico * SE

c(LI = LI, Media = media, LS = LS)
##       LI    Media       LS 
## 13.91001 14.49200 15.07399

También podemos pedirle a R el mismo resultado directamente:

t.test(datos$clorofila, conf.level = 0.95)
## 
##  One Sample t-test
## 
## data:  datos$clorofila
## t = 49.826, df = 59, p-value < 2.2e-16
## alternative hypothesis: true mean is not equal to 0
## 95 percent confidence interval:
##  13.91001 15.07399
## sample estimates:
## mean of x 
##    14.492

9.1 Visualización del intervalo de confianza

ic <- data.frame(
  grupo = "Ciénaga",
  media = media,
  LI = LI,
  LS = LS
)

ggplot(ic, aes(x = grupo, y = media)) +
  geom_point(size = 3) +
  geom_errorbar(
    aes(ymin = LI, ymax = LS),
    width = 0.10,
    linewidth = 0.9
  ) +
  geom_hline(
    yintercept = 12,
    linetype = 2
  ) +
  labs(
    title = "Media estimada e intervalo de confianza del 95 %",
    subtitle = "La línea horizontal representa el valor de referencia de 12 mg/m³",
    x = NULL,
    y = "Clorofila-a (mg/m³)"
  ) +
  theme_minimal(base_size = 13)

Interpretación. El intervalo proporciona un rango de valores plausibles para la media poblacional bajo el procedimiento utilizado. Es más informativo que reportar solo la media muestral porque muestra también la precisión de la estimación.


10 9. Una pregunta inferencial concreta

Supongamos que, por razones del problema de investigación, 12 mg/m³ se ha establecido como un valor de referencia y queremos estudiar si la media poblacional es mayor.

Planteamos:

\[ H_0:\mu=12 \]

frente a

\[ H_1:\mu>12. \]

prueba <- t.test(
  datos$clorofila,
  mu = 12,
  alternative = "greater"
)

prueba
## 
##  One Sample t-test
## 
## data:  datos$clorofila
## t = 8.568, df = 59, p-value = 3.017e-12
## alternative hypothesis: true mean is greater than 12
## 95 percent confidence interval:
##  14.00596      Inf
## sample estimates:
## mean of x 
##    14.492

Podemos extraer los elementos más importantes:

resultado_inferencia <- data.frame(
  Media = mean(datos$clorofila),
  DE = sd(datos$clorofila),
  t = unname(prueba$statistic),
  gl = unname(prueba$parameter),
  p_valor = prueba$p.value
)

knitr::kable(resultado_inferencia, digits = 4)
Media DE t gl p_valor
14.492 2.2529 8.568 59 0

Interpretación estadística. Si el valor p es pequeño respecto al nivel de significación elegido, los datos son poco compatibles con una media poblacional de 12 mg/m³ bajo el modelo del contraste.

Interpretación biológica. La conclusión debe volver siempre a la pregunta de investigación. No basta con escribir “p < 0.05”. Debemos expresar qué evidencia aportan los datos sobre la concentración media de clorofila-a en la ciénaga.


11 10. Integración final: qué aprendimos de los datos

El análisis completo siguió una secuencia lógica:

  1. Importamos y comprendimos la estructura de la base.
  2. Describimos las variables cualitativas con frecuencias y diagramas de barras.
  3. Describimos la clorofila-a mediante medidas de centro, posición y dispersión.
  4. Visualizamos su distribución mediante histograma, densidad, boxplot y violín.
  5. Exploramos diferencias entre grupos sin confundir diferencias descriptivas con evidencia inferencial.
  6. Exploramos relaciones entre variables cuantitativas mediante un diagrama de dispersión.
  7. Identificamos la limitación fundamental de la descripción: la muestra no es la población.
  8. Introdujimos el error estándar y el intervalo de confianza para cuantificar incertidumbre.
  9. Finalmente formulamos y evaluamos una pregunta inferencial.

La inferencia no aparece porque “ahora toca aplicar una prueba”. Aparece porque queremos pasar de lo que observamos en una muestra a una conclusión sobre una población y necesitamos cuantificar la incertidumbre de ese salto.


12 11. Actividad para el estudiante

Utilizando la misma base:

  1. Construya una tabla de frecuencias absoluta y relativa para habitat.
  2. Construya un diagrama de barras apropiado.
  3. Describa turbidez mediante media, mediana, DE, Q1, Q3, RIC y CV.
  4. Construya para turbidez un histograma, una curva de densidad y un boxplot.
  5. Compare descriptivamente la turbidez entre los tres sectores.
  6. Construya un diagrama de dispersión entre temperatura y clorofila.
  7. Escriba un párrafo de 6–8 líneas integrando resultados numéricos y gráficos.
  8. Explique, con sus propias palabras, por qué ese análisis descriptivo no basta para conocer exactamente el comportamiento de toda la ciénaga.