Módulo 4: Análisis Descriptivo y Exploratorio — Soluciones en R

Bioestadística Fundamental y Estadística Fundamental para las Ciencias de la Salud

Authors
Affiliations

Jose Miguel Leon Puentes

Universidad Nacional de Colombia

Johann David Sanchez Niño

Andrés Felipe Rache

Laura Camila Matheus Barrios

Brayan Stiven Martínez Rodríguez

Departamento de Estadística

Introducción

Este documento presenta la solución paso a paso, en R, de los 33 ejercicios del banco de práctica del Módulo 4 (ver archivo mod4_ejercicios.qmd). Para cada ejercicio se transcriben los datos exactamente como aparecen en la fuente, se muestra el código de R necesario para resolver cada parte, se explica en detalle qué hace cada línea y cada argumento relevante de las funciones utilizadas, y se cierra con una breve interpretación del resultado en el contexto del problema.

Ejercicio 1

Ver enunciado completo en el archivo de ejercicios: clasificación de 34 variables biológicas, ambientales y clínicas según su tipo (cuantitativa discreta, cuantitativa continua, cualitativa nominal, cualitativa ordinal).

Solución paso a paso

Este ejercicio es conceptual: no requiere cálculos ni código de R, sino aplicar las definiciones de tipo de variable a cada caso. La clasificación es la siguiente:

  • Cuantitativas discretas (se cuentan, toman valores enteros): número de brotes por m², recuento de células blancas, conteo de bacterias, recuento de especies de aves, conteo de colonias de microorganismos, recuento de células cancerosas, conteo de huevos de parásitos, recuento de especies de insectos, edad de los pacientes (cuando se mide en años cumplidos).
  • Cuantitativas continuas (se miden, toman cualquier valor en un rango): peso corporal, nivel de glucosa en sangre, presión arterial sistólica, nivel de colesterol en suero, concentración de contaminantes en el suelo, altura de las plantas de maíz, frecuencia cardíaca, temperatura corporal, nivel de pH, concentración de nutrientes en el suelo, concentración de contaminantes en el aire, volumen de producción de una cosecha, índice de masa corporal, nivel de radiación UV, nivel de humedad del suelo, tamaño de las hojas de una planta, concentración de oxígeno disuelto, frecuencia respiratoria.
  • Cualitativas ordinales (categorías con orden, pero sin distancias uniformes): puntaje en una escala de dolor, nivel de actividad física, categoría de riesgo cardiovascular.
  • Cualitativas nominales (categorías sin orden intrínseco): tipo de enfermedad diagnosticada, tipo de suelo, tipo de tratamiento médico recibido, método de cultivo utilizado.

Ejercicio 2

Encuesta de deporte favorito a 48 estudiantes; se pide tabla de frecuencias, diagrama de barras y diagrama circular.

Solución paso a paso

# 1) Datos:
deportes <- read.csv("mod4_E02_datos.csv")$deporte

read.csv("mod4_E02_datos.csv") lee el archivo de texto separado por comas que acompaña a este documento (debe estar guardado en la misma carpeta que el archivo .qmd) y lo carga como un data frame; $deporte extrae la única columna de ese data frame como un vector de caracteres, exactamente igual que si se hubiera escrito a mano con c("Fútbol", "Patinaje", ...).

El vector deportes guarda, en el mismo orden en que fueron encuestados, el deporte preferido de cada uno de los 48 estudiantes. Al ser texto entre comillas, R lo interpreta como un vector de tipo carácter (categórico), adecuado para una variable cualitativa nominal.

# a) Tablas de frecuencias absolutas y relativas
tabla_frec_abs <- table(deportes)              # Frecuencias absolutas
tabla_frec_rel <- round(prop.table(tabla_frec_abs), 3)  # Frecuencias relativas

table(deportes) cuenta cuántas veces aparece cada categoría única del vector y devuelve una tabla de frecuencias absolutas ordenada alfabéticamente por categoría. prop.table() toma esa tabla y divide cada conteo entre el total de observaciones, generando las frecuencias relativas (proporciones que suman 1). round(..., 3) redondea esas proporciones a 3 cifras decimales para facilitar su lectura.

# b) Diagrama de barras
barplot(tabla_frec_abs, main = "Diagrama de Barras - Deportes Preferidos",
        xlab = "Deporte", ylab = "Frecuencia Absoluta", col = "skyblue")

barplot() recibe directamente el objeto de tipo table y dibuja una barra por cada categoría, con altura igual a su frecuencia absoluta. El argumento main fija el título del gráfico, xlab y ylab etiquetan los ejes horizontal y vertical, y col = "skyblue" define el color de relleno de las barras.

# c) Diagrama circular (pastel) con porcentajes
porcentajes <- round(100 * tabla_frec_abs / sum(tabla_frec_abs), 1)
etiquetas <- paste(names(tabla_frec_abs), "\n", porcentajes, "%", sep = "")
pie(tabla_frec_abs, main = "Diagrama Circular - Deportes Preferidos",
    col = rainbow(length(tabla_frec_abs)), labels = etiquetas, cex = 0.8)

Primero se calcula el porcentaje de cada categoría dividiendo su frecuencia absoluta entre el total de encuestados y multiplicando por 100; round(..., 1) deja un decimal. paste() construye, para cada categoría, una etiqueta de texto que combina el nombre del deporte, un salto de línea ("\n") y su porcentaje, usando sep="" para no insertar espacios adicionales entre las piezas. Finalmente pie() dibuja el diagrama circular usando esas frecuencias, col = rainbow(...) asigna un color distinto a cada porción, labels coloca las etiquetas construidas y cex controla el tamaño del texto.

Interpretación

Fútbol y Voleyball son, junto con Ciclismo, los deportes con mayor frecuencia relativa (aproximadamente 20.8%, 20.8% y 18.8% respectivamente), mientras que Patinaje es el menos popular (8.3%). Para el diagrama de pastel, cada porcentaje equivale a \(\text{porcentaje} \times 3.6°\) del círculo (por ejemplo, Fútbol con 20.8% ocupa cerca de 75°).

Ejercicio 3

Determinar población, muestra, dato, unidad estadística, estadístico y parámetro en un estudio sobre consumo de alcohol y enfermedades hepáticas en La Pedregosa (Mérida).

Solución paso a paso

Al igual que Ejercicio 1, este es un ejercicio conceptual de identificación de los elementos del razonamiento estadístico; no requiere código de R porque no hay datos numéricos que procesar, sino conceptos que reconocer en el enunciado.

  • Población: todos los habitantes de la comunidad La Pedregosa, en la ciudad de Mérida.
  • Muestra: los habitantes de las 6 calles de La Pedregosa que fueron encuestados.
  • Dato: la respuesta individual de cada persona encuestada acerca de su consumo de bebidas alcohólicas y la presencia de enfermedades hepáticas.
  • Unidad estadística: un habitante de la comunidad La Pedregosa.
  • Estadístico: la proporción de sujetos encuestados que presentan enfermedades hepáticas asociadas al consumo de bebidas alcohólicas.
  • Parámetro: la proporción real (desconocida) de habitantes de La Pedregosa que presentan enfermedades hepáticas asociadas al consumo de bebidas alcohólicas.

Interpretación

El estadístico (calculado sobre la muestra de 6 calles) se usa como una estimación del parámetro poblacional (desconocido, correspondiente a toda La Pedregosa); esta distinción es la base de la inferencia estadística.

Ejercicio 4

Determinar población, muestra, dato, unidad estadística, estadístico y parámetro en un estudio sobre aceptación de un programa de salud en una comunidad rural de 4500 habitantes.

Solución paso a paso

Ejercicio conceptual, sin cómputo en R:

  • Población: los 4500 habitantes de la comunidad rural.
  • Muestra: las 250 familias encuestadas.
  • Dato: la respuesta de cada familia respecto a su aceptación del programa de salud.
  • Unidad estadística: una familia de la comunidad rural.
  • Estadístico: la proporción de familias encuestadas que están de acuerdo con el programa de salud (208/250).
  • Parámetro: la proporción real de familias en toda la comunidad que están de acuerdo con el programa de salud.

Interpretación

Con 208 de 250 familias a favor, el estadístico muestral es 208/250 = 0.832 (83.2%); este valor se usaría para estimar el parámetro poblacional desconocido en las 4500 familias/hogares de la comunidad completa.

Ejercicio 5

Niveles de glucosa en sangre de 100 niños en ayunas; se pide distribución de frecuencias, histograma, frecuencias relativas y polígono de frecuencias.

Solución paso a paso

# Datos de la muestra (n = 100). Al tratarse de un conjunto de datos grande,
# se cargan desde el archivo externo "mod4_E05_datos.csv" (una columna: "glucosa")
# en lugar de escribirlos directamente en el código.
x <- read.csv("mod4_E05_datos.csv")$glucosa

read.csv("mod4_E05_datos.csv") lee el archivo CSV (debe estar en la misma carpeta que este .qmd) y lo carga como un data frame de una columna; $glucosa extrae esa columna como un vector numérico, equivalente a escribir los 100 valores manualmente con c(...).

El vector x almacena los 100 valores de glucosa en sangre en el mismo orden en que fueron registrados, uno por cada niño de la muestra.

# 1) Distribución de frecuencias por intervalos
intervalos <- cut(x, breaks = seq(55, 85, by = 5), right = FALSE)
frecuencias_intervalos <- table(intervalos)
frecuencias_intervalos
intervalos
[55,60) [60,65) [65,70) [70,75) [75,80) [80,85) 
     16      20      34      15      10       5 

seq(55, 85, by = 5) genera los puntos de corte 55, 60, 65, …, 85, es decir, intervalos de amplitud 5. cut() clasifica cada valor de x dentro del intervalo que le corresponde; right = FALSE indica que los intervalos son cerrados por la izquierda y abiertos por la derecha ([55,60), [60,65), etc.). table() cuenta cuántas observaciones caen en cada intervalo, obteniendo la distribución de frecuencias absolutas.

# 2) Histograma
hist(x, main = "Histograma de niveles de glucosa en sangre en niños",
     xlab = "Glucosa en sangre", ylab = "Frecuencia", col = "lightblue", border = "black")

hist() construye automáticamente un histograma usando internamente intervalos similares a los anteriores; main, xlab y ylab fijan título y etiquetas de los ejes; col define el color de relleno de las barras y border el color del contorno de cada barra.

# 3) Frecuencias relativas
frecuencias_relativas <- prop.table(table(intervalos))
frecuencias_relativas
intervalos
[55,60) [60,65) [65,70) [70,75) [75,80) [80,85) 
   0.16    0.20    0.34    0.15    0.10    0.05 

prop.table() convierte la tabla de frecuencias absolutas en proporciones (cada valor dividido entre el total de 100 observaciones), mostrando qué fracción de los niños cae en cada intervalo de glucosa.

# 4) Polígono de frecuencias
h <- hist(x, plot = FALSE)  # Guarda la información del histograma sin dibujarlo
plot(h$mids, h$counts, type = "b", main = "Polígono de frecuencias",
     xlab = "Glucosa en sangre", ylab = "Frecuencia", col = "blue", pch = 16)

hist(x, plot = FALSE) calcula los límites, puntos medios (mids) y conteos (counts) de cada barra del histograma sin dibujarlo, y los guarda en el objeto h. plot(h$mids, h$counts, type = "b", ...) grafica los puntos medios de cada intervalo contra su frecuencia, uniendo los puntos con líneas (type = "b" dibuja puntos y líneas juntos), y pch = 16 define el símbolo (círculo relleno) usado para cada punto.

Interpretación

La mayoría de los niños tiene niveles de glucosa entre 60 y 70 mg/dL, con la distribución concentrada en la parte central y colas hacia valores más altos (75-85), lo que sugiere una ligera asimetría positiva en los niveles de glucosa en ayunas de esta muestra.

Ejercicio 6

Tabla estadística (histograma e histograma de frecuencias acumuladas) para las edades de 60 individuos.

Solución paso a paso

# Datos
edades <- read.csv("mod4_E06_datos.csv")$edad

read.csv("mod4_E06_datos.csv") carga el archivo de datos (guardado en la misma carpeta) como un data frame; $edad extrae la columna de edades como un vector numérico.

edades es el vector con las 60 edades registradas, en el mismo orden del enunciado.

# Crear intervalos de amplitud 10, límite superior cerrado
intervalos <- seq(floor(min(edades)), ceiling(max(edades)) + 5, by = 10)
frecuencias <- table(cut(edades, breaks = intervalos, right = TRUE, include.lowest = TRUE))
frecuencias

 [10,20]  (20,30]  (30,40]  (40,50]  (50,60]  (60,70]  (70,80]  (80,90] 
       3        2        3        4        7       10       16       12 
(90,100] 
       3 

floor(min(edades)) redondea hacia abajo la edad mínima y ceiling(max(edades)) + 5 redondea hacia arriba la máxima y le suma un margen, para asegurar que todos los datos queden cubiertos; seq(..., by = 10) genera los cortes cada 10 años. cut(..., right = TRUE, include.lowest = TRUE) construye intervalos cerrados por la derecha (por ejemplo, (10,20]) e incluye el valor mínimo exacto en el primer intervalo. table() cuenta cuántas edades caen en cada tramo de 10 años.

# a) Histograma
hist(edades, breaks = 10, col = "orchid", main = "Histograma de edades",
     xlab = "Edades", ylab = "Frecuencia", border = "black")

hist(edades, breaks = 10, ...) sugiere a R que use aproximadamente 10 intervalos para dividir el rango de edades; col = "orchid" colorea las barras, y main, xlab, ylab documentan el gráfico.

# b) Histograma de frecuencias acumuladas
frecuencias_acumuladas <- cumsum(frecuencias)
plot(intervalos[-length(intervalos)] + 5, frecuencias_acumuladas, type = "s",
     main = "Histograma de frecuencias acumuladas", xlab = "Edades",
     ylab = "Frecuencia acumulada", col = "red", lwd = 2)
grid()

cumsum(frecuencias) calcula la suma acumulada de las frecuencias absolutas, de modo que cada valor representa cuántas observaciones tienen edad menor o igual al límite superior de ese intervalo. intervalos[-length(intervalos)] + 5 toma los límites inferiores de cada intervalo (todos menos el último) y les suma 5 para ubicar aproximadamente el punto medio en el eje horizontal. plot(..., type = "s") dibuja un gráfico de tipo escalón, apropiado para representar frecuencias acumuladas; lwd = 2 engruesa la línea y grid() añade una cuadrícula de referencia.

Interpretación

La distribución de edades está concentrada entre 60 y 90 años (16 individuos en el intervalo 70-80 y 12 en el intervalo 80-90), y el gráfico de frecuencias acumuladas muestra que aproximadamente el 75% de la muestra tiene 70 años o menos.

Ejercicio 7

Consumo diario de grasas (gramos) de 120 hombres adultos; se pide distribución de frecuencias, histograma, media, mediana, varianza y desviación estándar.

Solución paso a paso

# Datos
datos <- read.csv("mod4_E07_datos.csv")$grasas_g

read.csv("mod4_E07_datos.csv") carga el archivo de datos como un data frame; $grasas_g extrae la columna con el consumo de grasas (g) de los 120 hombres como un vector numérico.

datos guarda las 120 mediciones de consumo diario de grasas en gramos, en el orden del enunciado.

# Distribución de frecuencias por intervalos
intervalos <- seq(floor(min(datos)), ceiling(max(datos)) + 5, by = 10)
frecuencias <- table(cut(datos, breaks = intervalos, right = FALSE, include.lowest = TRUE))

Se generan cortes cada 10 gramos entre el mínimo y el máximo (con un margen de seguridad de 5 al final), y cut(..., right = FALSE) construye intervalos cerrados a la izquierda ([22,32), [32,42), …). include.lowest = TRUE garantiza que el valor mínimo exacto quede incluido en el primer intervalo. table() cuenta las observaciones por intervalo.

# Medidas de tendencia central y dispersión
mean(datos)    # Media
[1] 97.75
median(datos)  # Mediana
[1] 96
var(datos)     # Varianza
[1] 860.2731
sd(datos)      # Desviación estándar
[1] 29.33041

mean(datos) suma todos los valores y los divide entre 120, dando el promedio del consumo de grasas. median(datos) ordena los datos y toma el valor central (o el promedio de los dos centrales, al ser 120 un número par). var(datos) calcula la varianza muestral (dividiendo la suma de cuadrados de las desviaciones entre \(n-1\)) y sd(datos) es su raíz cuadrada, es decir, la desviación estándar en las mismas unidades (gramos) que los datos originales.

Interpretación

La media de consumo de grasas es 97.75 g/día, muy cercana a la mediana (96 g/día), lo que sugiere una distribución razonablemente simétrica. La desviación estándar de 29.33 g indica una dispersión considerable en los hábitos de consumo de grasa entre los 120 hombres de la muestra, reflejada también en la varianza de 860.27 g².

Ejercicio 8

Distribución de edades (agrupada) de 75 casos de una enfermedad; se pide media, mediana, varianza y desviación estándar.

Solución paso a paso

# Datos agrupados: intervalos de edad y número de casos
intervalos <- c("5-14", "15-24", "25-34", "35-44", "45-54", "55-64")
casos <- c(5, 10, 20, 22, 13, 5)

intervalos guarda las 6 categorías de edad tal como aparecen en la tabla, y casos el número de pacientes reportado en cada una (total 75).

# Puntos medios de cada intervalo
punto_medio <- c((5+14)/2, (15+24)/2, (25+34)/2, (35+44)/2, (45+54)/2, (55+64)/2)
n_total <- sum(casos)

Como los datos están agrupados en intervalos, cada intervalo se representa por su marca de clase (punto medio), calculada como el promedio de sus límites inferior y superior. n_total <- sum(casos) suma todos los casos para obtener el tamaño total de la muestra (75).

# Media ponderada
media <- sum(punto_medio * casos) / n_total
media
[1] 35.23333

Para datos agrupados la media se calcula como un promedio ponderado: se multiplica cada marca de clase por su frecuencia (punto_medio * casos), se suman esos productos y se divide entre el total de observaciones n_total.

# Mediana para datos agrupados
frecuencia_acumulada <- cumsum(casos)
posicion_mediana <- n_total / 2
intervalo_mediana <- which(frecuencia_acumulada >= posicion_mediana)[1]
limite_inferior <- 25   # Límite inferior del intervalo que contiene la mediana (25-34)
frecuencia_intervalo <- casos[intervalo_mediana]
frecuencia_acum_antes <- ifelse(intervalo_mediana == 1, 0, frecuencia_acumulada[intervalo_mediana - 1])
ancho_intervalo <- 10
mediana <- limite_inferior + ((posicion_mediana - frecuencia_acum_antes) / frecuencia_intervalo) * ancho_intervalo
mediana
[1] 26.13636

Se calcula la frecuencia acumulada con cumsum(), se ubica la posición central n_total/2, y which(frecuencia_acumulada >= posicion_mediana)[1] identifica el primer intervalo cuya frecuencia acumulada alcanza o supera esa posición (el intervalo mediano). Luego se aplica la fórmula de interpolación lineal para datos agrupados: se parte del límite inferior del intervalo mediano y se avanza proporcionalmente según cuántas observaciones faltan para llegar a la mitad de los datos, dividido entre la frecuencia de ese intervalo y multiplicado por su amplitud (ancho_intervalo).

# Varianza y desviación estándar
varianza <- sum((punto_medio - media)^2 * casos) / n_total
varianza
[1] 165.7956
desviacion_estandar <- sqrt(varianza)
desviacion_estandar
[1] 12.87616

La varianza para datos agrupados se calcula ponderando el cuadrado de la desviación de cada marca de clase respecto a la media por su frecuencia, sumando esos productos y dividiendo entre el total de observaciones; sqrt() obtiene la desviación estándar como raíz cuadrada de la varianza.

Interpretación

La edad media de los casos reportados es 35.23 años, con mediana 26.14 años (la diferencia entre ambas sugiere asimetría positiva, es decir, hay casos en edades mayores que “estiran” la media hacia arriba). La desviación estándar de 12.88 años indica una dispersión amplia de edades entre los 75 casos registrados.

Ejercicio 9

Tiempo (minutos) que tardaron 75 empleados de un hospital en completar una tarea; se pide distribución de frecuencias, histograma, frecuencias relativas y polígono de frecuencias.

Solución paso a paso

# Datos
tiempos <- read.csv("mod4_E09_datos.csv")$tiempo_min

read.csv("mod4_E09_datos.csv") carga el archivo de datos como un data frame; $tiempo_min extrae la columna con los tiempos (min) como un vector numérico.

El vector tiempos guarda los 75 tiempos (en minutos) registrados para completar la tarea.

# a) Distribución de frecuencias por intervalos de amplitud 1
intervalos <- seq(floor(min(tiempos)), ceiling(max(tiempos)), by = 1)
frecuencias <- table(cut(tiempos, breaks = intervalos, right = FALSE, include.lowest = TRUE))
frecuencias

[1,2) [2,3) [3,4) [4,5) [5,6) [6,7] 
   14    26    15    10     5     5 

Se generan cortes de amplitud 1 minuto entre el mínimo (1) y el máximo (7) redondeados; cut(..., right = FALSE) produce intervalos como [1,2), [2,3), etc., y table() cuenta cuántos tiempos caen en cada uno.

# b) Histograma
hist(tiempos, breaks = intervalos, right = FALSE, col = "pink",
     main = "Histograma de tiempos de tarea", xlab = "Tiempo (minutos)",
     ylab = "Frecuencia", border = "black")

Se usa breaks = intervalos para que el histograma use exactamente los mismos cortes calculados antes, garantizando consistencia con la tabla de frecuencias; right = FALSE mantiene los mismos intervalos cerrados a la izquierda.

# c) Frecuencias relativas y relativas acumuladas
frecuencias_relativas <- prop.table(frecuencias)
frecuencias_relativas_acumuladas <- cumsum(frecuencias_relativas)
frecuencias_relativas

     [1,2)      [2,3)      [3,4)      [4,5)      [5,6)      [6,7] 
0.18666667 0.34666667 0.20000000 0.13333333 0.06666667 0.06666667 

prop.table() transforma las frecuencias absolutas en proporciones (cada una dividida por 75), y cumsum() sobre esas proporciones da la frecuencia relativa acumulada, es decir, qué fracción de empleados tardó como máximo un tiempo dado.

# d) Polígono de frecuencias
h <- hist(tiempos, breaks = intervalos, right = FALSE, plot = FALSE)
plot(h$mids, h$counts, type = "b", main = "Polígono de frecuencias",
     xlab = "Tiempo (minutos)", ylab = "Frecuencia", col = "lightblue", pch = 16, lwd = 2)
grid()

Igual que en Ejercicio 5, hist(..., plot = FALSE) calcula pero no dibuja el histograma, guardando los puntos medios (mids) y conteos (counts) de cada intervalo; plot(..., type = "b") conecta esos puntos con líneas y símbolos, formando el polígono de frecuencias.

Interpretación

La mayoría de los empleados (26 de 75) completó la tarea en el intervalo de 2 a 3 minutos, y la distribución muestra una cola larga hacia la derecha (algunos empleados tardaron entre 6 y 7 minutos), lo que indica asimetría positiva en los tiempos de ejecución de la tarea.

Ejercicio 10

Tiempo de ejercicio físico (minutos) de 96 mujeres durante una semana; se pide distribución de frecuencias, histograma, media, mediana, varianza y desviación estándar.

Solución paso a paso

# Datos
tiempo <- read.csv("mod4_E10_datos.csv")$tiempo_min

read.csv("mod4_E10_datos.csv") carga el archivo de datos como un data frame; $tiempo_min extrae la columna con los tiempos de ejercicio (min) como un vector numérico.

tiempo guarda las 96 mediciones de minutos de ejercicio físico semanal.

# a) Tabla de frecuencias (regla empírica log2 para el número de clases)
n <- length(tiempo)
num_clases <- ceiling(1 + log2(n))
rango <- range(tiempo)
amplitud <- ceiling((rango[2] - rango[1]) / num_clases)
cortes <- seq(from = min(tiempo), to = max(tiempo) + amplitud, by = amplitud)
frecuencia <- as.data.frame(table(cut(tiempo, breaks = cortes, include.lowest = TRUE, right = FALSE)))
colnames(frecuencia) <- c("Intervalo", "Frecuencia")
frecuencia
  Intervalo Frecuencia
1   [38,45)          9
2   [45,52)         10
3   [52,59)         12
4   [59,66)         14
5   [66,73)         16
6   [73,80)         17
7   [80,87)         16
8   [87,94]          2

length(tiempo) da el tamaño de la muestra (96). ceiling(1 + log2(n)) aplica una regla similar a la de Sturges para decidir el número de clases del histograma. range(tiempo) devuelve el mínimo y máximo, y con ello se calcula la amplitud de cada intervalo dividiendo el rango entre el número de clases (redondeando hacia arriba con ceiling). seq() genera los puntos de corte, cut() clasifica cada observación en su intervalo y table() cuenta las frecuencias; as.data.frame() y colnames() dan formato de tabla legible al resultado.

# b) Histograma
hist(tiempo, breaks = cortes, right = FALSE, col = "pink", border = "black",
     main = "Histograma del tiempo de ejercicio físico",
     xlab = "Tiempo de ejercicio (minutos)", ylab = "Frecuencia")

Se reutilizan los mismos cortes calculados en el paso anterior como argumento breaks de hist() para que el histograma sea coherente con la tabla de frecuencias.

# c) Medidas de tendencia central y dispersión
mean(tiempo)    # Media
[1] 65.375
median(tiempo)  # Mediana
[1] 66.5
var(tiempo)     # Varianza
[1] 193.0158
sd(tiempo)      # Desviación estándar
[1] 13.89301

Se aplican directamente sobre los datos sin agrupar (tiempo) las funciones mean(), median(), var() y sd(), que calculan respectivamente el promedio aritmético, el valor central de los datos ordenados, la varianza muestral (denominador \(n-1\)) y su raíz cuadrada.

Interpretación

Las mujeres de la muestra dedican en promedio 65.38 minutos de ejercicio a la semana (mediana 66.5 minutos, valores muy próximos entre sí, lo que indica una distribución cercana a la simetría). La desviación estándar de 13.89 minutos muestra una variabilidad moderada en los hábitos de ejercicio.

Ejercicio 11

Niveles de colesterol total (mg/dL) de 90 pacientes; se pide distribución de frecuencias, histograma, media, mediana, varianza y desviación estándar.

Solución paso a paso

# Datos
colesterol <- read.csv("mod4_E11_datos.csv")$colesterol_mgdl

read.csv("mod4_E11_datos.csv") carga el archivo de datos como un data frame; $colesterol_mgdl extrae la columna con los niveles de colesterol como un vector numérico.

colesterol almacena los 90 valores de colesterol total registrados en la clínica.

# a) Cálculo del número de clases (regla de Sturges) y amplitud de intervalos
n <- length(colesterol)
num_clases <- ceiling(1 + log2(n))
rango <- range(colesterol)
amplitud <- ceiling((rango[2] - rango[1]) / num_clases)
cortes <- seq(from = min(colesterol), to = max(colesterol) + amplitud, by = amplitud)
frecuencia <- as.data.frame(table(cut(colesterol, breaks = cortes, include.lowest = TRUE, right = FALSE)))
colnames(frecuencia) <- c("Intervalo", "Frecuencia")
frecuencia
  Intervalo Frecuencia
1 [150,159)          9
2 [159,168)         11
3 [168,177)         11
4 [177,186)         12
5 [186,195)         13
6 [195,204)         13
7 [204,213)         12
8 [213,222]          9

Igual que en Ejercicio 10, ceiling(1 + log2(n)) es la regla de Sturges para el número de intervalos, range() da el mínimo y máximo del colesterol, y con ello se calcula la amplitud y los cortes. cut() asigna cada paciente a su intervalo de colesterol y table() cuenta las frecuencias por intervalo.

# b) Histograma
hist(colesterol, breaks = cortes, right = FALSE, col = "orchid", border = "black",
     main = "Histograma de los niveles de colesterol", xlab = "Colesterol (mg/dL)",
     ylab = "Frecuencia")

hist() grafica las barras usando los mismos cortes de la tabla de frecuencias; col = "orchid" define el color de relleno.

# c) Medidas de tendencia central y dispersión
mean(colesterol)   # Media
[1] 186.1444
median(colesterol) # Mediana
[1] 187.5
var(colesterol)    # Varianza
[1] 392.462
sd(colesterol)     # Desviación estándar
[1] 19.81065

Como con los ejercicios anteriores, se aplican las funciones base de R sobre el vector de datos sin agrupar para obtener directamente la media, la mediana, la varianza muestral y la desviación estándar.

Interpretación

El colesterol medio de los 90 pacientes es 186.14 mg/dL, con mediana 187.5 mg/dL (valores muy cercanos, señal de una distribución aproximadamente simétrica). La desviación estándar de 19.81 mg/dL indica una dispersión moderada, coherente con niveles de colesterol que van de 150 a 220 mg/dL.

Ejercicio 12

Peso de la mazorca (kg) en 60 parcelas de maíz; se pide boxplot, media, moda, varianza, desviación estándar, coeficiente de variación y varios percentiles/cuartiles.

Solución paso a paso

# Datos de la muestra (n = 60), cargados desde el archivo externo
# "mod4_E12_datos.csv" (una columna: "peso_mazorca_kg").
datos <- read.csv("mod4_E12_datos.csv")$peso_mazorca_kg

read.csv("mod4_E12_datos.csv") carga el archivo de datos como un data frame; $peso_mazorca_kg extrae la columna con el peso de las mazorcas (kg) como un vector numérico.

El vector datos guarda los 60 pesos de mazorca (en kg) recolectados por el agrónomo.

# a) Diagrama de cajas y bigotes
boxplot(datos, main = "Diagrama de Cajas y Bigotes", ylab = "Peso de Mazorca (kg)")

boxplot(datos, ...) dibuja la caja delimitada por el primer y tercer cuartil, la línea de la mediana, los bigotes (hasta 1.5 veces el RIC) y marca con puntos los valores atípicos, si existen; ylab etiqueta el eje vertical con las unidades de la variable.

# b) Medidas de tendencia central y dispersión
media <- mean(datos)
moda <- as.numeric(names(table(datos)[which.max(table(datos))]))
varianza <- var(datos)
desviacion_estandar <- sd(datos)
coeficiente_variacion <- desviacion_estandar / media

mean(datos) calcula la media aritmética. Para la moda, table(datos) cuenta cuántas veces se repite cada valor, which.max() localiza la posición del valor con mayor frecuencia y names() devuelve ese valor como texto, que as.numeric() convierte de nuevo a número. var() y sd() calculan la varianza muestral y la desviación estándar. El coeficiente de variación se obtiene dividiendo la desviación estándar entre la media (sin multiplicar por 100 en este caso, quedando expresado como proporción).

# c) Percentiles, deciles y cuartiles
percentil_20 <- quantile(datos, 0.20)
decil_2 <- quantile(datos, 0.20)
mediana <- median(datos)
percentil_35 <- quantile(datos, 0.35)
cuartil_2 <- quantile(datos, 0.25)
cuartil_3 <- quantile(datos, 0.75)

quantile(datos, p) calcula el valor de la variable que deja una proporción p de las observaciones por debajo de él. El percentil 20 y el decil 2 son matemáticamente el mismo punto (0.20 de los datos), por eso ambas líneas usan probs = 0.20. median() calcula el valor central directamente (equivalente al percentil 50), y quantile(datos, 0.25) y quantile(datos, 0.75) dan el primer y tercer cuartil respectivamente.

Interpretación

La media del peso de mazorca es 6.39 kg y la moda es 6.3 kg, valores muy cercanos entre sí. El coeficiente de variación de aproximadamente 7.8% indica baja dispersión relativa: el rendimiento del cultivo es bastante homogéneo entre parcelas. El boxplot muestra un valor atípico superior (cerca de 7.8 kg), coherente con el máximo observado en los datos.

Ejercicio 13

Cantidad de medicamentos vendidos diariamente en una farmacia durante 31 días; se pide tabla de frecuencias discreta y medidas descriptivas para datos agrupados.

Solución paso a paso

# Datos de ventas diarias de medicamentos (n = 31), cargados desde el archivo
# externo "mod4_E13_datos.csv" (columnas: "dia", "cantidad_vendida").
datos_farmacia <- read.csv("mod4_E13_datos.csv")
dias <- datos_farmacia$dia
cantidad_vendida <- datos_farmacia$cantidad_vendida
datos_farmacia <- data.frame(Dia = dias, Cantidad_Vendida = cantidad_vendida)

read.csv("mod4_E13_datos.csv") carga el archivo con dos columnas (“dia” y “cantidad_vendida”) directamente como un data frame; $dia y $cantidad_vendida extraen cada columna como un vector, y luego se reconstruye datos_farmacia con los nombres de columna en mayúscula usados en el resto de la solución.

dias numera los 31 días del mes y cantidad_vendida guarda las unidades vendidas cada día. data.frame() combina ambos vectores en una tabla con una fila por día, facilitando el manejo conjunto de la información.

# a) Tabla de frecuencias discreta
tabla_frecuencias <- as.data.frame(table(datos_farmacia$Cantidad_Vendida))
colnames(tabla_frecuencias) <- c("Cantidad_Vendida", "Frec_Absoluta")
tabla_frecuencias$Frec_Absoluta_Acum <- cumsum(tabla_frecuencias$Frec_Absoluta)
tabla_frecuencias$Frec_Relativa <- tabla_frecuencias$Frec_Absoluta / sum(tabla_frecuencias$Frec_Absoluta)
tabla_frecuencias$Frec_Relativa_Acum <- cumsum(tabla_frecuencias$Frec_Relativa)
tabla_frecuencias$Porcentaje <- tabla_frecuencias$Frec_Relativa * 100
tabla_frecuencias$Cantidad_Vendida <- as.numeric(as.character(tabla_frecuencias$Cantidad_Vendida))
print(tabla_frecuencias)
   Cantidad_Vendida Frec_Absoluta Frec_Absoluta_Acum Frec_Relativa
1                12             1                  1    0.03225806
2                13             1                  2    0.03225806
3                15             1                  3    0.03225806
4                16             3                  6    0.09677419
5                17             2                  8    0.06451613
6                18             4                 12    0.12903226
7                19             4                 16    0.12903226
8                20             4                 20    0.12903226
9                21             3                 23    0.09677419
10               22             3                 26    0.09677419
11               23             2                 28    0.06451613
12               24             1                 29    0.03225806
13               25             2                 31    0.06451613
   Frec_Relativa_Acum Porcentaje
1          0.03225806   3.225806
2          0.06451613   3.225806
3          0.09677419   3.225806
4          0.19354839   9.677419
5          0.25806452   6.451613
6          0.38709677  12.903226
7          0.51612903  12.903226
8          0.64516129  12.903226
9          0.74193548   9.677419
10         0.83870968   9.677419
11         0.90322581   6.451613
12         0.93548387   3.225806
13         1.00000000   6.451613

table(datos_farmacia$Cantidad_Vendida) cuenta, para cada valor distinto de unidades vendidas, cuántos días se registró exactamente esa cantidad (frecuencia absoluta), ya que se trata de una variable discreta. cumsum() acumula las frecuencias absolutas y relativas. La frecuencia relativa se obtiene dividiendo cada frecuencia absoluta entre el total de días (31), y el porcentaje multiplica esa proporción por 100. as.numeric(as.character(...)) convierte la columna de categorías (que table() deja como texto/factor) de nuevo a formato numérico para poder operar con ella en los siguientes pasos.

# b) Media, moda, mediana, varianza y desviación estándar (datos agrupados)
media <- sum(tabla_frecuencias$Cantidad_Vendida * tabla_frecuencias$Frec_Relativa)

n_total <- sum(tabla_frecuencias$Frec_Absoluta)
posicion_mediana <- n_total / 2
mediana <- tabla_frecuencias$Cantidad_Vendida[which(tabla_frecuencias$Frec_Absoluta_Acum >= posicion_mediana)[1]]

moda <- tabla_frecuencias$Cantidad_Vendida[which.max(tabla_frecuencias$Frec_Absoluta)]

varianza <- sum(tabla_frecuencias$Frec_Absoluta * (tabla_frecuencias$Cantidad_Vendida - media)^2) / (n_total - 1)
desviacion_estandar <- sqrt(varianza)

La media ponderada se calcula multiplicando cada valor posible (Cantidad_Vendida) por su frecuencia relativa y sumando esos productos. Para la mediana, se ubica el primer valor cuya frecuencia absoluta acumulada alcanza o supera la mitad del total de observaciones (posicion_mediana). La moda corresponde al valor con mayor frecuencia absoluta, localizado con which.max(). La varianza pondera el cuadrado de la desviación de cada valor respecto a la media por su frecuencia absoluta, sumando y dividiendo entre \(n-1\); sqrt() da la desviación estándar.

Interpretación

En promedio se vendieron 19.32 medicamentos por día (mediana 19, moda 18), con una desviación estándar de 3.21 unidades. Esto indica que las ventas diarias de la farmacia son relativamente estables, oscilando la mayoría de los días entre 16 y 23 unidades vendidas.

Ejercicio 14

Frecuencia cardíaca (lpm) de 110 pacientes en UCI; se pide distribución de frecuencias continua y medidas descriptivas para datos agrupados.

Solución paso a paso

# Datos
frecuencia_cardiaca <- read.csv("mod4_E14_datos.csv")$frecuencia_cardiaca_lpm

read.csv("mod4_E14_datos.csv") carga el archivo de datos como un data frame; $frecuencia_cardiaca_lpm extrae la columna con las 110 mediciones como un vector numérico.

frecuencia_cardiaca guarda las 110 mediciones (latidos por minuto) de los pacientes.

# a) Tabla de frecuencias con intervalos (regla de Sturges)
n <- length(frecuencia_cardiaca)
k <- ceiling(1 + 3.322 * log10(n))
frecuencias <- cut(frecuencia_cardiaca, breaks = k, include.lowest = TRUE, right = TRUE)
tabla_frecuencias <- table(frecuencias)
tabla_frecuencias
frecuencias
[75,77.5] (77.5,80] (80,82.5] (82.5,85] (85,87.5] (87.5,90] (90,92.5] (92.5,95] 
       19        19        13        22        12        17         6         2 

ceiling(1 + 3.322 * log10(n)) es la fórmula clásica de la regla de Sturges para decidir el número de intervalos k en función del tamaño muestral. cut(frecuencia_cardiaca, breaks = k, ...) le pide a R que divida automáticamente el rango de los datos en k intervalos de igual amplitud; include.lowest = TRUE incluye el valor mínimo en el primer intervalo y right = TRUE hace que los intervalos sean cerrados a la derecha. table() cuenta las observaciones en cada intervalo.

# b) Medidas descriptivas para datos agrupados en intervalos
# Límites, marcas de clase y frecuencias
limites <- strsplit(gsub("\\(|\\)|\\[|\\]", "", names(tabla_frecuencias)), ",")
lim_inf <- as.numeric(sapply(limites, function(x) x[1]))
lim_sup <- as.numeric(sapply(limites, function(x) x[2]))
marca_clase <- (lim_inf + lim_sup) / 2
frec_abs <- as.numeric(tabla_frecuencias)

# Media
media <- sum(marca_clase * frec_abs) / n

# Moda (fórmula de interpolación)
indice <- which.max(frec_abs)
A <- lim_sup[indice] - lim_inf[indice]
f_ime1 <- ifelse(indice == 1, 0, frec_abs[indice - 1])
f_ima1 <- ifelse(indice == length(frec_abs), 0, frec_abs[indice + 1])
f_i <- frec_abs[indice]
moda <- lim_inf[indice] + (f_i - f_ime1) / ((f_i - f_ime1) + (f_i - f_ima1)) * A

# Mediana (fórmula de interpolación)
frec_acum <- cumsum(frec_abs)
n_2 <- n / 2
indice_med <- which(frec_acum > n_2)[1]
A2 <- lim_sup[indice_med] - lim_inf[indice_med]
F_i1 <- ifelse(indice_med == 1, 0, frec_acum[indice_med - 1])
f_i_med <- frec_abs[indice_med]
mediana <- lim_inf[indice_med] + (n_2 - F_i1) / f_i_med * A2

# Varianza y desviación estándar
varianza <- sum((marca_clase - media)^2 * frec_abs) / (n - 1)
desviacion_estandar <- sqrt(varianza)

Se extraen los límites inferior y superior de cada intervalo a partir de los nombres de la tabla de frecuencias (usando strsplit y gsub para eliminar los paréntesis/corchetes), y se calcula la marca de clase como el punto medio de cada intervalo. La media agrupada pondera cada marca de clase por su frecuencia absoluta y divide entre n. La moda usa la fórmula de interpolación de Pearson: localiza el intervalo modal (mayor frecuencia) con which.max() y ajusta su límite inferior según la diferencia de frecuencias con los intervalos vecinos. La mediana localiza, mediante la frecuencia acumulada, el intervalo donde se supera la mitad de las observaciones (n/2) e interpola linealmente dentro de ese intervalo. La varianza pondera el cuadrado de la desviación de cada marca de clase respecto a la media por la frecuencia absoluta correspondiente, dividiendo entre \(n-1\).

Interpretación

La frecuencia cardíaca media de los 110 pacientes es 82.93 lpm, con mediana 82.95 lpm y moda 83.02 lpm, valores muy próximos entre sí que indican una distribución aproximadamente simétrica. La desviación estándar de 4.87 lpm refleja una dispersión moderada alrededor del valor central, consistente con pacientes clínicamente estables en la UCI.

Ejercicio 15

Concentración de sodio en el sudor (mEq/l) de 60 estudiantes; se pide media, mediana, desviación típica (sin agrupar) y las mismas medidas para datos agrupados en intervalos.

Solución paso a paso

# Datos
sodio <- read.csv("mod4_E15_datos.csv")$sodio_meql

read.csv("mod4_E15_datos.csv") carga el archivo de datos como un data frame; $sodio_meql extrae la columna con la concentración de sodio como un vector numérico.

sodio guarda las 60 mediciones de concentración de sodio en el sudor, redondeadas al entero más cercano según el enunciado.

# a) Media, mediana y desviación típica (sin agrupar)
media <- mean(sodio)
mediana <- median(sodio)
desviacion <- sd(sodio)
media; mediana; desviacion
[1] 54.71667
[1] 53.5
[1] 11.88803

mean(), median() y sd() se aplican directamente sobre los 60 valores individuales, sin necesidad de agrupar en intervalos, para obtener la media, la mediana y la desviación típica exactas de la muestra.

# b) Agrupación en intervalos de clase (amplitud 5)
intervalos <- seq(floor(min(sodio)), ceiling(max(sodio)) + 5, by = 5)
frecuencias <- table(cut(sodio, breaks = intervalos, right = TRUE, include.lowest = TRUE))
puntos_medios <- (head(intervalos, -1) + tail(intervalos, -1)) / 2
frecuencias_absolutas <- as.numeric(frecuencias)

# Media agrupada
media_agrupada <- sum(puntos_medios * frecuencias_absolutas) / sum(frecuencias_absolutas)

# Mediana agrupada (interpolación)
N <- sum(frecuencias_absolutas)
F_acum <- cumsum(frecuencias_absolutas)
intervalo_mediana <- which(F_acum >= N / 2)[1]
Li <- intervalos[intervalo_mediana]
fi <- frecuencias_absolutas[intervalo_mediana]
F_ant <- ifelse(intervalo_mediana == 1, 0, F_acum[intervalo_mediana - 1])
amplitud <- diff(intervalos)[1]
mediana_agrupada <- Li + ((N / 2 - F_ant) / fi) * amplitud

# Desviación típica agrupada
media_cuadrada <- sum((puntos_medios^2) * frecuencias_absolutas) / sum(frecuencias_absolutas)
desviacion_tipica_agrupada <- sqrt(media_cuadrada - media_agrupada^2)

# Histograma
hist(sodio, breaks = intervalos, right = TRUE, col = "lightblue",
     main = "Histograma de la concentración de sodio",
     xlab = "Concentración de sodio (mEq/l)", ylab = "Frecuencia")

Se agrupan los datos en intervalos de amplitud 5 con cut() y table(); head(intervalos, -1) y tail(intervalos, -1) toman, respectivamente, todos los límites menos el último y todos menos el primero, y su promedio da el punto medio (marca de clase) de cada intervalo. La media agrupada pondera cada marca de clase por su frecuencia. Para la mediana agrupada se usa la fórmula de interpolación estándar (límite inferior del intervalo mediano más la fracción de observaciones que faltan para llegar a \(N/2\), multiplicada por la amplitud del intervalo). La desviación típica agrupada se obtiene con la fórmula computacional \(\sqrt{\overline{x^2} - \bar{x}^2}\), donde media_cuadrada es el promedio ponderado de los cuadrados de las marcas de clase. Finalmente hist() grafica el histograma usando los mismos intervalos.

Interpretación

Sin agrupar, la concentración media de sodio es 54.72 mEq/l (mediana 53.5, desviación típica 11.89). Al agrupar en intervalos de amplitud 5 se obtienen valores muy similares (media 54.25, mediana 53.23, desviación típica 11.81), lo que confirma que la agrupación no distorsiona significativamente las medidas descriptivas y valida el uso de la aproximación por intervalos cuando no se dispone del detalle individual.

Ejercicio 16

Glucosa en cordón umbilical de 1.371 recién nacidos (Tabla P.2.1, datos agrupados); se pide moda, mediana, media, desviación típica y percentil 5.

Solución paso a paso

# La tabla de frecuencias (21 clases) se carga desde el archivo externo
tabla16 <- read.csv("mod4_E16_datos.csv")
clases <- tabla16$glucosa_cordon
frecuencias <- tabla16$frecuencia

N <- sum(frecuencias)  # Total de frecuencias (1371)

# Punto medio de cada clase (intervalos de ancho 10, punto medio = clase + 5)
puntos_medios <- clases + 5

read.csv("mod4_E16_datos.csv") carga la tabla de frecuencias completa (21 clases) como un data frame de dos columnas; $glucosa_cordon y $frecuencia extraen cada columna por separado como vectores numéricos, reproduciendo exactamente clases y frecuencias tal como se usarían si se hubieran escrito a mano.

clases define los límites inferiores de los 21 intervalos de amplitud 10 usados en la Tabla P.2.1, y frecuencias el número de recién nacidos en cada uno. sum(frecuencias) confirma el total de la muestra (1371). Como todos los intervalos tienen ancho 10, el punto medio de cada uno es simplemente su límite inferior más 5.

# a) Media
media <- sum(puntos_medios * frecuencias) / N
media
[1] 71.11233

La media ponderada multiplica cada punto medio por su frecuencia, suma esos productos y divide entre el total N de recién nacidos.

# b) Mediana
frecuencias_acumuladas <- cumsum(frecuencias)
intervalo_mediana <- which(frecuencias_acumuladas >= N / 2)[1]
Li <- clases[intervalo_mediana]
fi <- frecuencias[intervalo_mediana]
F_ant <- ifelse(intervalo_mediana == 1, 0, frecuencias_acumuladas[intervalo_mediana - 1])
amplitud <- 10
mediana <- Li + ((N / 2 - F_ant) / fi) * amplitud
mediana
[1] 68.24468

Se acumulan las frecuencias con cumsum(), se localiza el intervalo donde la frecuencia acumulada alcanza la mitad de las observaciones, y se interpola linealmente usando el límite inferior de ese intervalo (Li), su frecuencia (fi) y la frecuencia acumulada del intervalo anterior (F_ant).

# c) Moda
intervalo_moda <- which.max(frecuencias)
L_moda <- clases[intervalo_moda]
f_moda <- frecuencias[intervalo_moda]
f_ant <- ifelse(intervalo_moda == 1, 0, frecuencias[intervalo_moda - 1])
f_sig <- ifelse(intervalo_moda == length(frecuencias), 0, frecuencias[intervalo_moda + 1])
moda <- L_moda + ((f_moda - f_ant) / ((f_moda - f_ant) + (f_moda - f_sig))) * amplitud
moda
[1] 59.49721

which.max(frecuencias) identifica el intervalo modal (mayor frecuencia absoluta). La fórmula de Pearson ajusta el límite inferior de ese intervalo según qué tanto mayor es su frecuencia respecto a la del intervalo anterior y siguiente, ponderado por la amplitud de clase.

# d) Desviación típica
media_cuadrada <- sum((puntos_medios^2) * frecuencias) / N
desviacion_tipica <- sqrt(media_cuadrada - media^2)
desviacion_tipica
[1] 21.88723
# e) Percentil 5
percentil_pos <- 0.05 * N
intervalo_percentil <- which(frecuencias_acumuladas >= percentil_pos)[1]
Li_percentil <- clases[intervalo_percentil]
fi_percentil <- frecuencias[intervalo_percentil]
F_ant_percentil <- ifelse(intervalo_percentil == 1, 0, frecuencias_acumuladas[intervalo_percentil - 1])
percentil_5 <- Li_percentil + ((percentil_pos - F_ant_percentil) / fi_percentil) * amplitud
percentil_5
[1] 42.27686

La desviación típica se calcula con la fórmula computacional \(\sqrt{\overline{x^2}-\bar{x}^2}\). Para el percentil 5, percentil_pos ubica el 5% del total de observaciones (\(0.05 \times 1371\)), se localiza el intervalo donde se alcanza esa posición acumulada, y se interpola de la misma manera que para la mediana, pero usando percentil_pos en lugar de \(N/2\).

Interpretación

La glucosa media en cordón umbilical es 71.11 mg/dL, con mediana 68.24, moda 59.50 y desviación típica 21.89 mg/dL. El percentil 5 (42.28 mg/dL) indica que solo el 5% de los 1371 recién nacidos presenta niveles de glucosa por debajo de ese valor, información relevante para detectar hipoglucemia neonatal.

Ejercicio 17

Coeficiente de variación de los pesos de recién nacidos (hospital B).

Solución paso a paso

# Datos
pesos <- read.csv("mod4_E17_datos.csv")$peso_g

read.csv("mod4_E17_datos.csv") carga el archivo de datos como un data frame; $peso_g extrae la columna con los pesos al nacer (g) como un vector numérico.

pesos guarda los 45 pesos de recién nacidos (en gramos) reportados para el hospital B, exactamente como aparecen en la tabla del enunciado.

# Media y desviación estándar
media <- mean(pesos)
desviacion <- sd(pesos)

# Coeficiente de variación (%)
cv <- (desviacion / media) * 100
cv
[1] 14.29617

mean(pesos) y sd(pesos) calculan la media y la desviación estándar de los pesos. El coeficiente de variación se obtiene dividiendo la desviación estándar entre la media y multiplicando por 100 para expresarlo como porcentaje, lo que permite comparar la dispersión relativa independientemente de las unidades.

Interpretación

El coeficiente de variación es aproximadamente 14.63%. Dado que el CV es moderado (por encima del umbral usual de 15% aunque cercano a él), podemos concluir que existe una variabilidad moderada en los pesos de los recién nacidos del hospital B: aunque hay una tendencia central clara (la media), los pesos individuales pueden desviarse de forma apreciable respecto a ese promedio.

Ejercicio 18

Comparación de dos métodos (A y B) de medición de glucosa en sangre, cada uno con 20 mediciones repetidas.

Solución paso a paso

# Datos de cada método
metodo_A <- c(140,141,142,127,138,136,135,142,126,148,
              139,142,141,151,144,146,145,148,147,136)
metodo_B <- c(130,132,146,138,145,148,147,135,136,137,
              141,146,138,131,134,146,139,140,148,146)

metodo_A y metodo_B guardan las 20 mediciones repetidas de glucosa obtenidas con cada método sobre la misma muestra.

# a) Estadísticos descriptivos de cada método
estadisticas_A <- data.frame(
  Media = mean(metodo_A), Mediana = median(metodo_A),
  Varianza = var(metodo_A), Desviacion_Tipica = sd(metodo_A))

estadisticas_B <- data.frame(
  Media = mean(metodo_B), Mediana = median(metodo_B),
  Varianza = var(metodo_B), Desviacion_Tipica = sd(metodo_B))

estadisticas_A
  Media Mediana Varianza Desviacion_Tipica
1 140.7   141.5 42.43158          6.513953
estadisticas_B
   Media Mediana Varianza Desviacion_Tipica
1 140.15   139.5 36.13421          6.011174

Para cada método se calculan, con las funciones base de R, la media, la mediana, la varianza y la desviación típica, y se organizan en un data.frame de una sola fila para facilitar la comparación visual entre ambos métodos.

# b) Gráfico comparativo (boxplot)
datos <- data.frame(
  Metodo = rep(c("A", "B"), each = 20),
  Glucosa = c(metodo_A, metodo_B))

boxplot(Glucosa ~ Metodo, data = datos, col = c("salmon", "turquoise"),
        main = "Comparación de los métodos A y B",
        xlab = "Método", ylab = "Glucosa (mg/dL)")

rep(c("A","B"), each = 20) crea una columna que repite “A” 20 veces y luego “B” 20 veces, para identificar a qué método pertenece cada medición dentro del data.frame combinado datos. La fórmula Glucosa ~ Metodo en boxplot() le indica a R que dibuje una caja separada de la variable Glucosa para cada nivel de la variable Metodo, permitiendo comparar directamente ambas distribuciones.

Interpretación

Ambos métodos arrojan resultados de glucosa similares en promedio (media A = 140.7, media B = 140.15). El método B tiene menor variabilidad (varianza 36.13 frente a 42.43 del método A), lo que sugiere que es ligeramente más preciso, mientras que la mediana algo más alta del método A (141.5 frente a 139.5) podría indicar una leve tendencia a sobreestimar los valores de glucosa en sangre.

Ejercicio 19

Pesos (kg) de 10 animales experimentales sometidos a cirugía; se pide media, mediana, varianza, desviación estándar y coeficiente de variación.

Solución paso a paso

# Datos del ejercicio
pesos <- c(13.2, 15.4, 13.0, 16.6, 16.9, 14.4, 13.6, 15.0, 14.6, 13.1)

pesos guarda los 10 pesos (en kg) de los animales, transcritos directamente de la tabla del enunciado.

# a) Media
mean(pesos)
[1] 14.58
# b) Mediana
median(pesos)
[1] 14.5
# c) Varianza
var(pesos)
[1] 1.988444
# d) Desviación estándar
sd(pesos)
[1] 1.410122
# e) Coeficiente de variación
(sd(pesos) / mean(pesos)) * 100
[1] 9.67162

Se aplican, en orden, mean() para la media aritmética, median() para el valor central de los datos ordenados, var() para la varianza muestral, sd() para la desviación estándar y, por último, el cociente sd(pesos) / mean(pesos) multiplicado por 100 para expresar la dispersión relativa como coeficiente de variación porcentual.

Interpretación

El peso promedio de los animales es 14.58 kg (mediana 14.5 kg), con una desviación estándar de 1.41 kg y un coeficiente de variación de apenas 4.3%. Este CV tan bajo indica que los pesos de los 10 animales son muy homogéneos, con poca dispersión relativa alrededor del promedio.

Ejercicio 20

Zinc (mg/l) en 653 individuos sanos (datos agrupados); se pide media, mediana, desviación típica, percentiles 5 y 95 y coeficiente de variación.

Solución paso a paso

# Datos del ejercicio
intervalos <- c("<=10.0", "10.1-11", "11.1-12", "12.1-13", "13.1-14",
                 "14.1-15", "15.1-16", ">16")
frecuencias <- c(25, 41, 108, 251, 126, 43, 40, 19)
total <- sum(frecuencias)

# Puntos medios de los intervalos
puntos_medios <- c(10.0, 10.6, 11.6, 12.6, 13.6, 14.6, 15.6, 16.5)

frecuencias guarda el número de individuos en cada categoría de zinc, y sum(frecuencias) confirma el total (653). Los puntos_medios fueron aproximados a partir de los límites de cada intervalo (para las categorías abiertas “≤10.0” y “>16” se usan aproximaciones razonables basadas en la amplitud típica de las demás clases).

# Media
media <- sum(puntos_medios * frecuencias) / total
media
[1] 12.83139

La media pondera cada punto medio por su frecuencia y divide entre el total de individuos.

# Mediana
frecuencia_acumulada <- cumsum(frecuencias)
mediana_intervalo <- which(frecuencia_acumulada >= total / 2)[1]
limite_inferior <- puntos_medios[mediana_intervalo] - 0.5
frecuencia_clase <- frecuencias[mediana_intervalo]
frecuencia_acumulada_anterior <- ifelse(mediana_intervalo == 1, 0, frecuencia_acumulada[mediana_intervalo - 1])
amplitud <- 1.0
mediana <- limite_inferior + ((total / 2 - frecuencia_acumulada_anterior) / frecuencia_clase) * amplitud
mediana
[1] 12.70757

Se acumulan las frecuencias, se localiza el intervalo donde se supera la mitad de las 653 observaciones, y se interpola linealmente dentro de ese intervalo usando su límite inferior aproximado (puntos_medios - 0.5), su frecuencia y la amplitud de clase (1.0 en este caso).

# Varianza y desviación típica
varianza <- sum((puntos_medios - media)^2 * frecuencias) / (total - 1)
desviacion_tipica <- sqrt(varianza)

# Coeficiente de variación
coef_variacion <- (desviacion_tipica / media) * 100

La varianza pondera el cuadrado de la desviación de cada punto medio respecto a la media por su frecuencia, dividiendo entre \(total - 1\); la raíz cuadrada da la desviación típica, y su cociente con la media (multiplicado por 100) da el coeficiente de variación.

# Percentiles 5 y 95
percentil <- function(p) {
  posicion <- p / 100 * total
  intervalo <- which(frecuencia_acumulada >= posicion)[1]
  limite_inferior <- puntos_medios[intervalo] - 0.5
  frecuencia_clase <- frecuencias[intervalo]
  frecuencia_acumulada_anterior <- ifelse(intervalo == 1, 0, frecuencia_acumulada[intervalo - 1])
  amplitud <- 1.0
  resultado <- limite_inferior + ((posicion - frecuencia_acumulada_anterior) / frecuencia_clase) * amplitud
  return(resultado)
}
percentil(5)
[1] 10.28659
percentil(95)
[1] 15.75875

Se define una función percentil(p) que generaliza el cálculo anterior de la mediana a cualquier percentil p: calcula la posición correspondiente (p/100 del total), localiza el intervalo donde se alcanza esa posición y aplica la misma fórmula de interpolación lineal. Llamar a percentil(5) y percentil(95) da directamente el percentil 5 y el percentil 95 de la distribución del zinc.

Interpretación

El nivel medio de zinc es 12.83 mg/l, con mediana 12.71 y desviación típica 1.44 mg/l, lo que da un coeficiente de variación de aproximadamente 11.2% (dispersión relativa baja). El intervalo entre el percentil 5 (10.29 mg/l) y el percentil 95 (15.76 mg/l) contiene al 90% central de los individuos sanos de la muestra, un rango de referencia útil en la práctica clínica.

Ejercicio 21

Número de visitas a un Centro de Salud realizadas por 1.000 individuos (datos agrupados); se pide moda, mediana, media, desviación típica y percentiles 5 y 95.

Solución paso a paso

# La tabla de frecuencias
tabla21 <- read.csv("mod4_E21_datos.csv")
visitas <- tabla21$visitas
frecuencias <- tabla21$frecuencia
total <- sum(frecuencias)

read.csv("mod4_E21_datos.csv") carga la tabla de frecuencias completa (21 valores de número de visitas) como un data frame de dos columnas; $visitas y $frecuencia extraen cada columna como vectores numéricos.

visitas enumera el número de visitas posibles de 0 a 19, agregando 20 como valor representativo de la categoría “20 o más”. frecuencias guarda cuántos de los 1000 individuos reportaron cada número de visitas, y sum(frecuencias) confirma el total de la muestra.

# a) Moda
moda <- visitas[which.max(frecuencias)]
moda
[1] 0

which.max(frecuencias) localiza la posición del valor de visitas con mayor frecuencia, y se usa para indexar el vector visitas y obtener la moda.

# b) Mediana
frecuencia_acumulada <- cumsum(frecuencias)
mediana_posicion <- total / 2
mediana_intervalo <- which(frecuencia_acumulada >= mediana_posicion)[1]
mediana <- visitas[mediana_intervalo]
mediana
[1] 0

Al tratarse de una variable discreta (no de intervalos continuos), la mediana se obtiene directamente localizando el primer valor cuya frecuencia acumulada alcanza o supera la mitad del total de observaciones, sin necesidad de interpolar.

# c) Media
media <- sum(visitas * frecuencias) / total
media
[1] 2.159

La media pondera cada número de visitas por su frecuencia absoluta y divide entre el total de 1000 individuos.

# d) Desviación típica
varianza <- sum((visitas - media)^2 * frecuencias) / (total - 1)
desviacion_tipica <- sqrt(varianza)
desviacion_tipica
[1] 3.776239
# e) Percentiles 5 y 95
percentil <- function(p) {
  posicion <- p / 100 * total
  intervalo <- which(frecuencia_acumulada >= posicion)[1]
  return(visitas[intervalo])
}
percentil(5)
[1] 0
percentil(95)
[1] 10

La varianza pondera el cuadrado de la desviación de cada valor de visitas respecto a la media por su frecuencia, dividiendo entre \(total - 1\). Para los percentiles, la función percentil(p) ubica la posición correspondiente al p% del total y devuelve directamente el valor discreto de visitas en el que se alcanza esa posición acumulada (sin interpolación, por tratarse de datos discretos).

Interpretación

La moda y la mediana del número de visitas son ambas 0 (la mitad de la población no visitó el centro de salud durante el período de estudio), mientras que la media es 2.159 visitas, notablemente más alta debido a la fuerte asimetría positiva generada por el grupo de alta utilización (“20 o más” visitas). La desviación típica de 3.78 visitas confirma la gran dispersión: mientras el percentil 5 es 0, el percentil 95 alcanza 10 visitas, mostrando que un pequeño grupo de pacientes concentra un número desproporcionado de consultas.

Ejercicio 22

Edad (meses) de estudiantes de Medicina (Universidad de los Andes); se pide varianza, desviación típica, asimetría y curtosis.

Solución paso a paso

# Datos
edades <- read.csv("mod4_E22_datos.csv")$edad_meses

read.csv("mod4_E22_datos.csv") carga el archivo de datos como un data frame; $edad_meses extrae la columna de edades (en meses) como un vector numérico.

edades guarda los 42 valores de edad en meses de los estudiantes de Medicina.

# a) Varianza y desviación típica
var(edades)
[1] 1128.048
sd(edades)
[1] 33.58642

var(edades) calcula la varianza muestral y sd(edades) su raíz cuadrada (desviación típica), usando las funciones base de R directamente sobre el vector de datos.

# b) Asimetría y curtosis
library(e1071)
skewness(edades)
[1] -0.02224996
kurtosis(edades)
[1] -1.388365

Se carga la librería e1071, que provee las funciones skewness() y kurtosis(). skewness(edades) calcula el coeficiente de asimetría de Fisher (tercer momento estandarizado); por defecto usa type = 3. kurtosis(edades) calcula el exceso de curtosis (cuarto momento estandarizado menos 3), de modo que un valor de 0 corresponde a una distribución mesocúrtica similar a la normal.

Interpretación

La varianza es 1128.05 meses² (desviación típica 33.59 meses). El coeficiente de asimetría es -0.022, prácticamente cero, lo que indica que la distribución de edades es aproximadamente simétrica. La curtosis de -1.39 es negativa, señalando una distribución platicúrtica: más aplanada y con colas más ligeras que una distribución normal, es decir, las edades están más dispersas de manera uniforme en lugar de concentrarse fuertemente alrededor de la media.

Ejercicio 23

Niveles de glucosa en ayunas de 60 pacientes; se pide asimetría, curtosis, histograma y boxplot.

Solución paso a paso

# Datos de la muestra (n = 60), cargados desde el archivo externo
# "mod4_E23_datos.csv" (una columna: "glucosa_mgdl").
glucosa <- read.csv("mod4_E23_datos.csv")$glucosa_mgdl

read.csv("mod4_E23_datos.csv") carga el archivo de datos como un data frame; $glucosa_mgdl extrae la columna con los niveles de glucosa como un vector numérico.

glucosa guarda los 60 valores de glucosa en ayunas (mg/dL) reclutados para el estudio.

# a) Coeficientes de asimetría y curtosis
library(e1071)
asimetria <- skewness(glucosa)
asimetria
[1] 0.1483909
curtosis <- kurtosis(glucosa)
curtosis
[1] -1.03484

skewness(glucosa) mide la asimetría del tercer momento: un valor positivo indica una cola más larga hacia valores altos, y uno negativo hacia valores bajos. kurtosis(glucosa) mide el exceso de curtosis del cuarto momento respecto a la distribución normal.

# b) Histograma
hist(glucosa, main = "Histograma de niveles de glucosa", xlab = "Niveles de glucosa (mg/dL)",
     ylab = "Frecuencia", col = "#FF1493", border = "black")

hist() construye automáticamente el histograma de los 60 valores de glucosa, col fija el color de relleno (magenta) y border el contorno negro de cada barra.

# c) Boxplot
boxplot(glucosa, main = "Boxplot de niveles de glucosa", ylab = "Niveles de glucosa (mg/dL)",
        col = "lightgreen", border = "darkgreen")

boxplot(glucosa, ...) dibuja el diagrama de caja y bigotes de la variable, con col para el relleno de la caja y border para el color de sus líneas.

Interpretación

El coeficiente de asimetría es 0.148 (ligeramente positivo, cercano a la simetría) y la curtosis es -1.03 (negativa, distribución platicúrtica, más aplanada que la normal). El histograma y el boxplot confirman una distribución relativamente uniforme de la glucosa entre 25 y 80 mg/dL, sin valores atípicos evidentes, lo cual es coherente con una población de riesgo pero sin casos extremos aislados.

Ejercicio 24

Índice de masa corporal (IMC) de 75 mujeres adultas; se pide asimetría, curtosis, histograma y boxplot.

Solución paso a paso

# Datos
imc <- read.csv("mod4_E24_datos.csv")$imc

read.csv("mod4_E24_datos.csv") carga el archivo de datos como un data frame; $imc extrae la columna del índice de masa corporal como un vector numérico.

imc guarda los 75 valores de índice de masa corporal registrados en el estudio.

# a) Coeficientes de asimetría y curtosis
library(e1071)
asimetria <- skewness(imc)
asimetria
[1] -0.03126053
curtosis <- kurtosis(imc)
curtosis
[1] -1.062001

Igual que en Ejercicio 22 y Ejercicio 23, skewness() y kurtosis() de la librería e1071 calculan los coeficientes de forma de la distribución del IMC.

# b) Histograma
hist(imc, breaks = seq(floor(min(imc)), ceiling(max(imc)), by = 1), col = "#EEE0E5",
     border = "black", main = "Histograma del IMC", xlab = "Índice de Masa Corporal",
     ylab = "Frecuencia")

breaks = seq(floor(min(imc)), ceiling(max(imc)), by = 1) fuerza que las barras del histograma tengan una amplitud de 1 unidad de IMC, replicando el efecto de binwidth = 1 usado en el gráfico original; se usan floor() y ceiling() para redondear el mínimo hacia abajo y el máximo hacia arriba, garantizando que la secuencia de cortes cubra completamente el rango de los datos (si se usara min(imc) y max(imc) directamente, la secuencia podría no llegar exactamente al valor máximo y R produciría un error); col define el color de relleno.

# c) Boxplot
boxplot(imc, main = "Boxplot del IMC", ylab = "Índice de Masa Corporal",
        col = "#B03060")

boxplot(imc, ...) dibuja la caja y bigotes de la variable IMC, permitiendo visualizar su mediana, cuartiles y posibles valores atípicos.

Interpretación

El coeficiente de asimetría es -0.131 (muy cercano a cero, distribución prácticamente simétrica) y la curtosis es -0.85 (levemente platicúrtica). El histograma muestra una distribución en forma de campana ligeramente aplanada, centrada alrededor de 28-29 kg/m², sin valores atípicos marcados en el boxplot.

Ejercicio 25

Niveles de triglicéridos (mg/dL) de 40 pacientes; se pide asimetría, curtosis, histograma y boxplot.

Solución paso a paso

# Datos
tri <- read.csv("mod4_E25_datos.csv")$trigliceridos_mgdl

read.csv("mod4_E25_datos.csv") carga el archivo de datos como un data frame; $trigliceridos_mgdl extrae la columna de triglicéridos como un vector numérico.

tri guarda los 40 niveles de triglicéridos (mg/dL) registrados en el programa de manejo de lípidos.

# a) Coeficientes de asimetría y curtosis
library(e1071)
asimetria <- skewness(tri)
asimetria
[1] 0.401084
curtosis <- kurtosis(tri)
curtosis
[1] -0.7785716

Se aplican skewness() y kurtosis() de la librería e1071 sobre el vector de triglicéridos, obteniendo respectivamente el coeficiente de asimetría y el exceso de curtosis de la distribución.

# b) Histograma
hist(tri, main = "Histograma de Triglicéridos", xlab = "Nivel de Triglicéridos (mg/dL)",
     col = "#FFBBFF", border = "black")

hist(tri, ...) construye el histograma de los niveles de triglicéridos; col fija el color de relleno de las barras.

# c) Boxplot
boxplot(tri, main = "Boxplot de Triglicéridos", ylab = "Nivel de Triglicéridos (mg/dL)",
        col = "#FFE4E1")

boxplot(tri, ...) dibuja el diagrama de caja y bigotes de los triglicéridos, mostrando su mediana y dispersión.

Interpretación

El coeficiente de asimetría es 0.40 (positivo, cola algo más larga hacia valores altos de triglicéridos) y la curtosis es -0.78 (levemente platicúrtica). El histograma confirma una ligera concentración de pacientes en niveles bajos-medios de triglicéridos (180-200 mg/dL) con una cola hacia valores más altos (hasta 240 mg/dL), consistente con la asimetría positiva calculada.

Ejercicio 26

Relación entre índice de masa corporal (IMC) y presión arterial sistólica (PAS) en 55 adultos; se pide gráfico de dispersión y coeficiente de correlación de Pearson.

Solución paso a paso

# Datos
datos26 <- read.csv("mod4_E26_datos.csv")
IMC <- datos26$imc
PAS <- datos26$pas

read.csv("mod4_E26_datos.csv") carga el archivo con dos columnas (“imc” y “pas”) como un data frame; $imc y $pas extraen cada variable como un vector numérico independiente, conservando el emparejamiento observación por observación.

IMC y PAS guardan, respectivamente, el índice de masa corporal y la presión arterial sistólica de los 55 adultos, en el mismo orden (es decir, el elemento i de cada vector corresponde a la misma persona).

# a) Gráfico de dispersión
plot(IMC, PAS, main = "Gráfico de dispersión entre IMC y PAS",
     xlab = "Índice de Masa Corporal (IMC)", ylab = "Presión Arterial Sistólica (PAS)",
     col = "#00F5FF", pch = 19)

plot(IMC, PAS, ...) dibuja un punto por cada persona, ubicando su IMC en el eje horizontal y su PAS en el eje vertical; pch = 19 usa un círculo relleno como símbolo y col define su color.

# b) Coeficiente de correlación de Pearson
correlacion <- cor(IMC, PAS)
correlacion
[1] 0.986754

cor(IMC, PAS) calcula, por defecto (method = "pearson"), el coeficiente de correlación lineal de Pearson entre ambas variables, dividiendo su covarianza entre el producto de sus desviaciones estándar.

Interpretación

El coeficiente de correlación de Pearson es 0.88, lo cual indica que existe una correlación lineal ascendente fuerte entre el índice de masa corporal y la presión arterial sistólica: a mayor IMC, mayor tiende a ser la PAS en esta muestra de 55 adultos.

Ejercicio 27

Relación entre edad y capacidad vital pulmonar (CVP) en 70 personas sanas; se pide gráfico de dispersión y coeficiente de correlación de Pearson.

Solución paso a paso

# Datos
datos27 <- read.csv("mod4_E27_datos.csv")
edad <- datos27$edad
cvp <- datos27$cvp

read.csv("mod4_E27_datos.csv") carga el archivo con dos columnas (“edad” y “cvp”) como un data frame; $edad y $cvp extraen cada variable como un vector numérico independiente, conservando el emparejamiento observación por observación.

edad y cvp guardan la edad (años) y la capacidad vital pulmonar (litros) de las 70 personas, en correspondencia elemento a elemento.

# a) Gráfico de dispersión
plot(edad, cvp, main = "Gráfico de dispersión entre Edad y CVP",
     xlab = "Edad (años)", ylab = "Capacidad Vital Pulmonar (L)", col = "orchid", pch = 19)

plot(edad, cvp, ...) grafica cada persona como un punto (edad en el eje X, CVP en el eje Y), permitiendo observar visualmente la tendencia entre ambas variables.

# b) Coeficiente de correlación de Pearson
cor(edad, cvp)
[1] -0.9947384

cor(edad, cvp) calcula el coeficiente de correlación lineal de Pearson entre la edad y la capacidad vital pulmonar.

Interpretación

El coeficiente de correlación de Pearson es aproximadamente -0.995, lo cual indica una correlación lineal descendente muy fuerte entre las variables edad y CVP: a medida que aumenta la edad, la capacidad vital pulmonar disminuye de forma casi perfectamente lineal, un patrón esperado fisiológicamente por la pérdida progresiva de función pulmonar con el envejecimiento.

Ejercicio 28

Relación entre frecuencia cardíaca en reposo (FCR) y capacidad aeróbica (\(VO_2\) máx) en 46 personas; se pide gráfico de dispersión y coeficiente de correlación de Pearson.

Solución paso a paso

# Datos
datos28 <- read.csv("mod4_E28_datos.csv")
fcr <- datos28$fcr_lpm
vo2_max <- datos28$vo2max

read.csv("mod4_E28_datos.csv") carga el archivo con dos columnas (“fcr_lpm” y “vo2max”) como un data frame; $fcr_lpm y $vo2max extraen cada variable como un vector numérico independiente, conservando el emparejamiento observación por observación.

fcr y vo2_max almacenan, respectivamente, la frecuencia cardíaca en reposo (lpm) y el consumo máximo de oxígeno (mL/kg/min) de las 46 personas evaluadas.

# a) Gráfico de dispersión
plot(fcr, vo2_max, main = "Gráfico de dispersión: FCR vs. VO2 máx",
     xlab = "Frecuencia Cardíaca en Reposo (lpm)", ylab = "VO2 máx (mL/kg/min)",
     pch = 19, col = "pink")

plot(fcr, vo2_max, ...) ubica cada persona como un punto según su FCR (eje X) y su VO2 máx (eje Y); pch = 19 dibuja círculos rellenos.

# b) Coeficiente de correlación de Pearson
cor(fcr, vo2_max)
[1] -0.996713

cor(fcr, vo2_max) calcula el coeficiente de correlación de Pearson entre la frecuencia cardíaca en reposo y la capacidad aeróbica máxima.

Interpretación

El coeficiente de correlación de Pearson es aproximadamente -0.997, lo cual indica una correlación lineal descendente muy fuerte: las personas con menor frecuencia cardíaca en reposo tienden a tener mayor capacidad aeróbica (\(VO_2\) máx), un hallazgo consistente con el efecto del entrenamiento cardiovascular sobre la eficiencia del corazón.

Ejercicio 29

Tabla de contingencia entre Tipo de Dieta y Control de la Presión Arterial en 50 personas; se pide calcular los perfiles fila.

Solución paso a paso

# Crear la tabla de contingencia
tabla <- matrix(c(18, 7,
                   10, 5,
                   6, 4),
                 nrow = 3, byrow = TRUE)

rownames(tabla) <- c("Baja en Sodio", "Baja en Grasas", "Vegetariana")
colnames(tabla) <- c("Controlada", "No Controlada")
tabla
               Controlada No Controlada
Baja en Sodio          18             7
Baja en Grasas         10             5
Vegetariana             6             4

matrix(c(...), nrow = 3, byrow = TRUE) construye una matriz de 3 filas por 2 columnas, llenándola por filas (byrow = TRUE) con los conteos observados de la tabla de contingencia. rownames() y colnames() asignan las etiquetas de las categorías de dieta y de control de presión arterial a las filas y columnas, respectivamente.

# Calcular totales por fila y perfiles fila
totales_fila <- rowSums(tabla)
perfiles_fila <- sweep(tabla, 1, totales_fila, FUN = "/")
perfiles_fila
               Controlada No Controlada
Baja en Sodio   0.7200000     0.2800000
Baja en Grasas  0.6666667     0.3333333
Vegetariana     0.6000000     0.4000000

rowSums(tabla) suma los valores de cada fila, dando el total de personas en cada tipo de dieta (25, 15 y 10). sweep(tabla, 1, totales_fila, FUN = "/") divide cada elemento de la matriz entre el total de su fila correspondiente (el argumento 1 indica que se opera por filas), obteniendo así los perfiles fila: la proporción de personas controladas y no controladas dentro de cada tipo de dieta.

Interpretación

El 72% de las personas con dieta baja en sodio tienen la presión controlada, frente a 66.67% en dieta baja en grasas y 60% en dieta vegetariana. Existe una asociación clara entre seguir una dieta controlada y tener restricciones dietéticas como baja en sodio o baja en grasas, siendo esta asociación ligeramente menos marcada para la dieta vegetariana.

Ejercicio 30

Tabla de contingencia entre Grupo Sanguíneo y Respuesta a la Vacuna en 70 personas; se pide calcular los perfiles fila.

Solución paso a paso

# Crear la tabla de contingencia
tabla <- matrix(c(12, 8,
                   16, 9,
                   6, 4,
                   10, 5),
                 nrow = 4, byrow = TRUE,
                 dimnames = list("Grupo Sanguíneo" = c("A", "B", "AB", "O"),
                                  "Respuesta a la Vacuna" = c("Positiva", "Negativa")))
tabla
               Respuesta a la Vacuna
Grupo Sanguíneo Positiva Negativa
             A        12        8
             B        16        9
             AB        6        4
             O        10        5

matrix(..., dimnames = list(...)) construye la tabla de contingencia de 4 grupos sanguíneos por 2 tipos de respuesta, asignando simultáneamente los nombres de fila y columna mediante el argumento dimnames, que recibe una lista con las etiquetas de cada dimensión.

# Totales por fila y perfiles fila
totales_fila <- rowSums(tabla)
perfiles_fila <- sweep(tabla, 1, totales_fila, FUN = "/")
perfiles_fila
               Respuesta a la Vacuna
Grupo Sanguíneo  Positiva  Negativa
             A  0.6000000 0.4000000
             B  0.6400000 0.3600000
             AB 0.6000000 0.4000000
             O  0.6666667 0.3333333

Igual que en E29, rowSums() obtiene el total de personas de cada grupo sanguíneo (20, 25, 10 y 15) y sweep(tabla, 1, totales_fila, FUN = "/") divide cada celda entre el total de su fila para obtener la proporción de respuesta positiva y negativa dentro de cada grupo sanguíneo.

Interpretación

Las proporciones de respuesta positiva son bastante similares entre los grupos sanguíneos (entre 60% y 67%, correspondientes a A: 60%, B: 64%, AB: 60% y O: 66.7%). Con base en estos datos, no hay evidencia de una relación importante entre el grupo sanguíneo y la respuesta a la vacuna; es decir, el grupo sanguíneo no parece ser un factor determinante en la efectividad o reacción a la vacuna en cuestión.

Ejercicio 31

Tabla de contingencia entre Uso de Medicamentos y Frecuencia de Hospitalización en 50 personas; se pide calcular los perfiles fila.

Solución paso a paso

# Crear la tabla de contingencia
tabla <- matrix(c(12, 18,
                   5, 15),
                 nrow = 2, byrow = TRUE,
                 dimnames = list("Uso de Medicamentos" = c("Sí", "No"),
                                  "Frecuencia de Hospitalización" = c("Alta", "Baja")))
tabla
                   Frecuencia de Hospitalización
Uso de Medicamentos Alta Baja
                 Sí   12   18
                 No    5   15

Se construye una matriz de 2x2 con los conteos observados de uso de medicamentos frente a frecuencia de hospitalización, usando nuevamente dimnames para etiquetar filas y columnas de forma legible.

# Totales por fila y perfiles fila
totales_fila <- rowSums(tabla)
perfiles_fila <- sweep(tabla, 1, totales_fila, FUN = "/")
perfiles_fila
                   Frecuencia de Hospitalización
Uso de Medicamentos Alta Baja
                 Sí 0.40 0.60
                 No 0.25 0.75

rowSums(tabla) da el total de personas que sí usan medicamentos (30) y las que no (20). sweep() divide cada celda entre el total de su fila para obtener la proporción de hospitalización alta y baja dentro de cada grupo de uso de medicamentos.

Interpretación

Las personas que usan medicamentos con frecuencia tienen una mayor probabilidad de ser hospitalizadas con frecuencia (40%) en comparación con aquellas que no usan medicamentos con frecuencia (25%). Existe una relación positiva entre el uso frecuente de medicamentos y la frecuencia de hospitalización, lo que sugiere que las personas que consumen muchos medicamentos tienden a ser hospitalizadas con más frecuencia (posiblemente porque ambas variables reflejan una condición de salud más comprometida).

Ejercicio 32

Concentración de mercurio en el hígado de 28 delfines de franjas machos; se pide gráfico de cajas y bigotes e identificación de valores atípicos.

Solución paso a paso

# Datos
concentraciones_mercurio <- read.csv("mod4_E32_datos.csv")$mercurio_ugg

read.csv("mod4_E32_datos.csv") carga el archivo de datos como un data frame; $mercurio_ugg extrae la columna con las concentraciones de mercurio como un vector numérico.

concentraciones_mercurio guarda las 28 mediciones de mercurio (microgramos/gramo) en el hígado de los delfines, tal como aparecen en la tabla del enunciado.

# Construir el gráfico de cajas y bigotes
boxplot(concentraciones_mercurio,
        main = "Concentraciones de Mercurio en Delfines de Franjas Machos",
        ylab = "Concentración de Mercurio (microgramos/gramo)",
        col = "lightblue")

boxplot() dibuja la caja delimitada por el primer y tercer cuartil de las concentraciones de mercurio, con la mediana marcada en el interior, los bigotes extendiéndose hasta 1.5 veces el rango intercuartílico desde cada cuartil, y puntos individuales para cualquier observación que caiga fuera de ese rango (valores atípicos); col define el color de relleno de la caja.

Interpretación

El boxplot no marca puntos fuera de los bigotes, por lo que, pese a la gran dispersión de los datos (desde valores cercanos a 1.7 hasta 485 microgramos/gramo), no se identifican valores atípicos según el criterio de 1.5×RIC: toda la variabilidad observada, aunque amplia, se considera parte del comportamiento normal de bioacumulación de mercurio en esta muestra de delfines.

Ejercicio 33

Nivel de polvo de cadmio en el aire (mg/m³), muestra de 35 lecturas; se pide diagrama de tallo y hojas, histograma de frecuencias relativas y polígono de frecuencias relativas acumuladas (ojiva).

Solución paso a paso

# Datos
datos <- read.csv("mod4_E33_datos.csv")$cadmio_mgm3

read.csv("mod4_E33_datos.csv") carga el archivo de datos como un data frame; $cadmio_mgm3 extrae la columna con el nivel de cadmio como un vector numérico.

datos guarda las 35 lecturas de concentración de cadmio (mg de cadmio por m³ de aire), tal como fueron reportadas en el enunciado.

# a) Diagrama de tallo y hojas
stem(datos)

  The decimal point is 2 digit(s) to the left of the |

  2 | 0
  3 | 007999
  4 | 022445679
  5 | 01122345678
  6 | 0111226
  7 | 0

stem(datos) construye automáticamente un diagrama de tallo y hojas: toma los dos primeros dígitos significativos de cada valor (después del “0.0”) como “tallo” (2, 3, 4, 5, 6 o 7, correspondientes a los rangos 0.02x, 0.03x, …, 0.07x) y el último dígito como “hoja”, mostrando de un vistazo tanto la forma de la distribución como los valores individuales.

# b) Histograma de frecuencias relativas
hist(datos, freq = FALSE, main = "Histograma de Frecuencias Relativas",
     xlab = "Nivel de Cadmio", ylab = "Frecuencia RelativaX100")

freq = FALSE le indica a hist() que en lugar de graficar frecuencias absolutas en el eje Y, grafique densidades (equivalentes a frecuencias relativas escaladas), de modo que el área total de las barras sea igual a 1. Esto permite comparar la forma de la distribución independientemente del tamaño de la muestra.

# c) Polígono de frecuencias relativas acumuladas (ojiva)
frec_relativas_acum <- cumsum(table(datos)) / length(datos)
plot(names(frec_relativas_acum), frec_relativas_acum, type = "l",
     main = "Polígono de Frecuencias Relativas Acumuladas", xlab = "Nivel de Cadmio",
     ylab = "Frecuencia Relativa Acumulada")

table(datos) cuenta las repeticiones de cada valor único, cumsum() acumula esas frecuencias y al dividir entre length(datos) (35) se obtiene la frecuencia relativa acumulada, es decir, la proporción de lecturas menores o iguales a cada valor. plot(..., type = "l") dibuja esa proporción acumulada como una línea continua, formando la ojiva.

Interpretación

El diagrama de tallo y hojas muestra que los tallos 4 y 5 (niveles 0.04x y 0.05x) concentran la mayoría de las lecturas, y el histograma de frecuencias relativas confirma una forma aproximadamente acampanada, aunque con una ligera asimetría hacia la derecha (cola hacia valores más altos como 0.070). La ojiva asciende de forma relativamente suave y constante entre 0.02 y 0.07 mg/m³, sin saltos abruptos, lo que es consistente con una distribución razonablemente continua del nivel de cadmio en el aire monitoreado.

Referencias

  • Chipia Lobo, J. F. (2018). Prácticas de ejercicios y problemas de bioestadística. Mérida, Venezuela.
  • Daniel, W. W. (2002). Bioestadística: base para el análisis de las ciencias de la salud (4a ed.). Limusa Wiley.
  • López-Kleine, L. (2012). Bioestadística. Universidad Nacional de Colombia.
  • Martín Andrés, A., y Luna del Castillo, J. D. (2004). Bioestadística para las ciencias de la salud. Norma-Capitel.
  • Matheus Barrios, L. C. (2024). Solucionario - Repositorio de ejercicios de estadística [documento de monitoría]. Universidad Nacional de Colombia, Departamento de Estadística.
  • Pérez Flórez, M. (2021). Manual de bioestadística y demografía. Pontificia Universidad Javeriana.
  • Rache, A. F. (s.f.) - Solucionario Banco de Ejercicios – Bioestadística Fundamental [documento de monitoría]. Departamento de Estadística, Universidad Nacional de Colombia.
  • Environmental Management. (1981, septiembre). Informe citado en ejercicio sobre polvo de cadmio, pág. 414.

Copyright © 2026 - Created with Quarto