De toy datasets a mini-proyecto de investigación con R
Objetivos de aprendizaje
- Descubrir y cargar toy datasets integrados en R y en paquetes populares.
- Explorar y resumir variables clave con funciones base y tidyverse.
- Plantear una pregunta de investigación clara y viable con datos de juguete.
- Diseñar un flujo de trabajo reproducible desde la exploración hasta la visualización.
- Comunicar hallazgos en un breve informe R Markdown.
Índice de contenidos
1. Introducción a los toy datasets en R
2. Selección del dataset y planteamiento del problema
3. Exploración inicial y limpieza mínima
4. Análisis descriptivo guiado
5. Visualización rápida con ggplot2
6. De la pregunta a la mini-investigación
1. Introducción a los toy datasets en R
1.1 ¿Qué es un toy dataset y por qué usarlo?
Un toy dataset (conjunto de datos “de juguete”) es un conjunto pequeño, limpio y bien documentado que viene incluido en R base o en paquetes muy difundidos. Está pensado para practicar funciones, demostrar conceptos estadísticos y construir ejemplos reproducibles sin la sobrecarga de data wrangling complejo. En esencia, son “arenas de juego” seguras donde puedes:
- Aprender sintaxis sin preocuparte de permisos, licencias o tamaños gigantes.
- Probar modelos de manera rápida antes de aplicarlos a datos reales más ruidosos.
- Compartir código reproducible: cualquier persona con R puede ejecutar el mismo script y obtener idénticos resultados.
- Enseñar estadística o programación sin exponer datos sensibles.
- Comparar métodos (base R vs. tidyverse, etc.) en igualdad de condiciones.
En clase: Usaremos toy datasets para construir un pipeline completo —desde la exploración hasta la visualización— y luego extrapolaremos la metodología a proyectos con datos reales.
Un pipeline es una secuencia ordenada de pasos que automatiza el recorrido de los datos desde que se cargan hasta que se presentan los resultados. Cada etapa —limpieza, transformación, análisis y visualización— toma la salida de la fase anterior y la usa como entrada, de modo que el flujo avanza sin intervención manual repetitiva. Esto garantiza reproducibilidad (cualquier persona puede volver a ejecutar todo el proceso y obtener los mismos resultados) y eficiencia (las tareas se encadenan de forma clara y lógica, evitando errores de “copiar-pegar” o trabajos duplicados).
1.2 Principales fuentes integradas (datasets, palmerpenguins, gapminder, etc.)
A continuación se listan los paquetes más populares que distribuyen toy datasets de acceso inmediato, junto con un vistazo rápido a su contenido principal y una línea de código para cargarlos.
| Paquete / Dataset | Tamaño (filas × variables) | Temática | Carga rápida |
|---|---|---|---|
R base – datasets | ≈ 20+ datasets (iris 5×150, mtcars 11×32…) | Botánica, automóviles, series temporales | data(iris) |
palmerpenguins – penguins | 344 × 8 | Mediciones morfológicas de pingüinos en la Antártica | library(palmerpenguins) |
gapminder – gapminder | 1 704 × 6 | Indicadores socio-económicos (1952-2007) | library(gapminder) |
nycflights13 – flights | 336 776 × 19 | Vuelos que salen de NYC en 2013 | library(nycflights13) |
babynames – babynames | ≈ 1,9 M × 5 | Nombres de bebés en EE. UU. (1880-2022) | library(babynames) |
Para descubrir qué datasets incluye cualquier paquete, basta con inspeccionar su documentación:
library(help = "datasets") # lista todo lo integrado en R base data(package = "palmerpenguins")# lista datasets del paquete indicado
En las siguientes secciones elegiremos uno de estos conjuntos, formularemos una pregunta de investigación y recorreremos todo el flujo analítico hasta generar un mini-informe reproducible.
2. Selección del dataset y planteamiento del problema
2.1 Criterios de selección: tamaño, temática y variables
Antes de lanzarte a programar conviene detenerte y elegir cuidadosamente el toy dataset que usarás. Un buen punto de partida asegura que el resto del flujo (el pipeline) sea fluido y que las conclusiones tengan sentido. A continuación se resumen los criterios prácticos más importantes:
| Criterio | Preguntas guía | Consejo rápido |
|---|---|---|
| Tamaño (n filas × variables) | ¿Puedes examinar las primeras filas a simple vista? ¿Tarda más de 1 s en cargarse? | Para ejercicios de aula, busca < 10 000 filas y < 50 columnas. |
| Temática o contexto | ¿Te resulta interesante o relevante? ¿Hay un público que valore los hallazgos? | Elige un tema que motive: ecología (pingüinos), salud, economía, etc. |
| Variable objetivo | ¿Tienes claro qué variable quieres explicar, comparar o predecir? ¿Es numérica o categórica? | Asegúrate de que exista al menos una variable dependiente claramente definida. |
| Calidad y documentación | ¿Incluye ?dataset_name con descripción completa?¿Conoces la unidad de medida y las categorías? | Prefiere datasets bien documentados para evitar supuestos dudosos. |
| Variedad de tipos | ¿Contiene algo de “todo”: numérico, categórico, fechas? ¿Permite practicar filtros, tablas y gráficos distintos? | Cuanto más heterogéneo, más rico para ejercitar distintas funciones. |
| Ausencia de valores perdidos | ¿La columna clave tiene muchos NA?¿Necesitarás limpieza pesada? | En un toy dataset conviene minimizar la fase de limpieza para centrarse en el análisis. |
Si un conjunto cumple la mayoría de estos criterios, lo más probable es que puedas ir del “hello world” al informe final en una sola sesión de trabajo.
2.2 Ejemplos de preguntas de investigación “pequeñas pero potentes”
La clave de un mini-proyecto exitoso no es la complejidad, sino la claridad. Tu pregunta debe ser lo bastante concreta para responderla en unas cuantas líneas de código, pero lo bastante interesante para contar una historia. Aquí van ideas listas para usar o adaptar:
| Dataset | Pregunta sugerida | Tipo de análisis |
|---|---|---|
iris | ¿Qué especie muestra mayor variabilidad en el largo del sépalo? | Descriptivo + boxplot |
palmerpenguins::penguins | ¿Difieren significativamente los pesos medios entre Adélie y Gentoo? | t-test + violín plot |
gapminder | ¿Cómo cambió la esperanza de vida en Latinoamérica entre 1952 y 2007? | Línea temporal + resumen por año |
mtcars | ¿Los autos con transmisión automática consumen más combustible que los manuales? | Gráfico de densidad + prueba U de Mann-Whitney |
nycflights13::flights | ¿Cuál de los tres aeropuertos de NYC presenta el mayor retraso promedio en enero? | Filtrado + tabla de medias |
Observa que cada pregunta cumple tres requisitos básicos:
- Alcance limitado: una o dos variables principales.
- Acción clara: comparar, describir o correlacionar.
- Resultado interpretable: la respuesta cabe en un par de frases y un gráfico.
Tu turno: Elige un dataset, revisa sus variables con str() y formula tu propia pregunta en una sola línea. Ese será el eje de tu mini-proyecto.
3. Exploración inicial y limpieza mínima
3.1 Inspección de estructura con str() y skimr::skim()
El primer paso de cualquier pipeline es mirar los datos: confirmar cuántas filas y columnas hay, qué tipos de variable existen y si aparecen NA. Dos funciones cubren la mayoría de necesidades iniciales:
| Función | Qué muestra | Cuándo usarla |
|---|---|---|
str() | Clase del objeto, número de filas, tipo y muestra de cada columna. | Exploración rápida en cualquier instalación base de R. |
skimr::skim() | Resumen extendido: missing, media, p50, p99, long/short strings… | Diagnóstico completo que evita calcular summary() por separado. |
Ejemplo con palmerpenguins::penguins
# Cargar paquetes library(palmerpenguins) # datos library(skimr) # resumen extendido # 1. Vista estructural rápida str(penguins) # 2. Resumen extendido skim(penguins)
La salida de str() revela, por ejemplo, que species y sex son factores con varios NA, mientras que bill_length_mm es numérica. Con skim() verás de inmediato valores faltantes y estadísticas útiles como cuartiles y número de categorías únicas.
3.2 Tratamiento rápido de valores faltantes y tipos de datos
Los toy datasets ya vienen bastante limpios, pero casi siempre verás algo de NA o columnas mal tipadas (por ejemplo, años como números cuando conviene factor). La estrategia exprés consiste en:
- Detectar NA por variable.
- Eliminar o imputar solo si son pocos y no arruinan la muestra.
- Corregir tipos con
as.factor(),as.numeric(), etc.
1 • Localizar los NA
# Conteo de NA por columna colSums(is.na(penguins))
2 • Decidir: ¿quitar filas o imputar?
Para un análisis exploratorio breve es aceptable eliminar filas con valores faltantes en las variables clave:
library(dplyr) penguins_clean <- penguins %>% tidyr::drop_na(bill_length_mm, bill_depth_mm, sex)
Si el porcentaje de NA supera ~10 % en una variable importante, sustitúyelos por un valor “seguro” (media, mediana o categoría “Desconocido”):
# Reemplazar NA en 'sex' por "unknown" penguins$sex <- forcats::fct_explicit_na(penguins$sex, na_level = "unknown")
3 • Ajustar tipos de datos
Cuando la variable representa un grupo finito (año de estudio, isla, transmisión), conviene usar factor; cuando sea código ID, conviértelo a character para evitar promedios absurdos.
| Situación | Código de corrección | Motivo |
|---|---|---|
| Año que actúa como categoría | penguins$year <- as.factor(penguins$year) | Permite facetas y barras por año sin orden numérico engañoso. |
| Columna numérica leída como texto | df$ingreso <- as.numeric(df$ingreso) | Habilita cálculos de media y desviación estándar. |
| ID con ceros a la izquierda | df$id <- as.character(df$id) | Evita que “0012” se convierta en 12. |
Ejemplo completo de limpieza mínima
penguins_min <- penguins %>% # 1. Conservar solo variables clave select(species, island, sex, bill_length_mm, body_mass_g) %>% # 2. Eliminar filas con NA en variables numéricas drop_na(bill_length_mm, body_mass_g) %>% # 3. Reetiquetar NA en 'sex' mutate(sex = forcats::fct_explicit_na(sex, na_level = "unknown")) %>% # 4. Asegurar que 'island' sea factor ordenado alfabéticamente mutate(island = factor(island, levels = sort(unique(island))))
Después de esta limpieza mínima el dataset queda listo para describir, modelar o visualizar sin interferencias de NA ni tipos inconsistentes. El objetivo no es perfeccionar cada celda, sino despejar rápidamente los obstáculos que impedirían avanzar al análisis.
4. Análisis descriptivo guiado
4.1 Tablas de frecuencia y medidas de tendencia central
Con el dataset ya depurado llega el momento de responder la primera batería de preguntas descriptivas: ¿Qué tan frecuente es cada categoría? y ¿Dónde se concentra la distribución de las variables numéricas?
4.1.1 Frecuencias con table() y dplyr::count()
library(dplyr) # Frecuencia absoluta de especies freq_species <- table(penguins_min$species) freq_species # La misma operación, versión tidy penguins_min %>% count(species, name = "n") %>% mutate(pct = round(n / sum(n) * 100, 1))
| species | n | % |
|---|---|---|
| Adelie | 152 | 44.9 |
| Chinstrap | 68 | 20.1 |
| Gentoo | 119 | 35.0 |
A simple vista se aprecia que Adelie es la especie dominante (~45 %), mientras que Chinstrap es la menos representada. Esta tabla de dos columnas (n y %) suele bastar para el informe final.
4.1.2 Tendencia central para variables numéricas
Las medidas de tendencia central más habituales se obtienen con mean() y median(). Para tablas múltiples usaremos dplyr::summarise().
penguins_min %>%
summarise(
n = n(),
bill_mean_mm = mean(bill_length_mm, na.rm = TRUE),
bill_median_mm = median(bill_length_mm, na.rm = TRUE),
mass_mean_g = mean(body_mass_g, na.rm = TRUE),
mass_median_g = median(body_mass_g, na.rm = TRUE)
)
| n | bill_mean_mm | bill_median_mm | mass_mean_g | mass_median_g |
|---|---|---|---|---|
| 339 | 43.9 | 44.5 | 4201 | 4050 |
Observa la ligera diferencia entre media (43.9 mm) y mediana (44.5 mm) para bill_length_mm; su cercanía indica que no existen valores extremos exagerados en esta variable.
4.2 Comparaciones básicas entre grupos con dplyr
Después del paneo general viene la comparación: ¿cambia el patrón según especie, sexo o isla? Gracias a dplyr podemos agrupar y resumir en una sola línea.
4.2.1 Media y mediana por especie
penguins_min %>%
group_by(species) %>%
summarise(
count = n(),
bill_avg = round(mean(bill_length_mm), 1),
mass_med = median(body_mass_g)
)
| species | count | bill_avg (mm) | mass_med (g) |
|---|---|---|---|
| Adelie | 152 | 38.8 | 3700 |
| Chinstrap | 68 | 48.8 | 3700 |
| Gentoo | 119 | 47.6 | 5075 |
La Gentoo destaca por un body mass claramente más alto (mediana ≈ 5 kg), mientras que Adelie presenta picos de longitud de pico menores. Estos contrastes ya apuntan a hipótesis sobre diferenciación ecológica.
4.2.2 Tabla rápida de diferencias por sexo
penguins_min %>%
group_by(sex) %>%
summarise(across(c(bill_length_mm, body_mass_g),
list(mean = ~mean(.x, na.rm = TRUE),
sd = ~sd(.x, na.rm = TRUE)),
.names = "{.col}_{.fn}"))
| sex | bill_length_mm_mean | bill_length_mm_sd | body_mass_g_mean | body_mass_g_sd |
|---|---|---|---|---|
| female | 42.1 | 4.5 | 3862 | 560 |
| male | 45.9 | 5.1 | 4545 | 775 |
| unknown | 44.4 | 4.8 | 4219 | 628 |
Los machos en promedio presentan picos más largos y mayor masa, diferencias coherentes con la literatura biológica. La columna unknown —creada en la limpieza— permite mantener la muestra completa sin descartar registros.
4.2.3 Rango intercuartílico por isla
Para variables susceptibles de sesgo, como body_mass_g, conviene usar el rango intercuartílico (IQR) como medida de dispersión robusta:
penguins_min %>%
group_by(island) %>%
summarise(
mass_iqr = IQR(body_mass_g, na.rm = TRUE)
)
Un IQR elevado indica mayor heterogeneidad de tamaños entre individuos de la misma isla: un punto de partida para explorar nichos ecológicos o gradientes de alimento.
Próximo paso: Si alguna de estas diferencias te resulta llamativa, avanza a pruebas de hipótesis (t-test, ANOVA) o visualizaciones (ggplot2) para confirmar y comunicar tus hallazgos.
5. Visualización rápida con ggplot2
Una vez obtenidas las tablas y descriptivos, la forma más eficaz de comunicar patrones es con una buena gráfica. El paquete ggplot2 —incluido en el tidyverse— ofrece una sintaxis coherente basada en la gramática de los gráficos, lo que facilita la producción de figuras claras y reproducibles.
5.1 Gráficos fundamentales para variables categóricas y numéricas
5.1.1 Barras para una variable categórica
library(ggplot2)
# Distribución de especies
ggplot(penguins_min, aes(x = species, fill = species)) +
geom_bar() +
labs(title = "Distribución de especies de pingüinos",
x = "Especie", y = "Frecuencia") +
scale_fill_brewer(palette = "Set2") +
theme_minimal()
geom_bar()cuenta registros automáticamente (stat = "count").- La paleta
Set2es apta para daltónicos y da contraste inmediato.
5.1.2 Histograma para una variable numérica
ggplot(penguins_min, aes(x = body_mass_g)) +
geom_histogram(binwidth = 200, fill = "#4682B4", colour = "white") +
labs(title = "Distribución de masa corporal",
x = "Masa (g)", y = "Número de individuos") +
theme_classic()
Elegir un binwidth apropiado evita “picos” artificiales. La regla de Silverman o la de Freedman–Diaconis pueden automatizar la selección, pero un valor redondo (200 g) suele ser suficiente para toy datasets.
5.1.3 Boxplot comparativo (numérico ✕ categórico)
ggplot(penguins_min, aes(x = species, y = body_mass_g, fill = species)) +
geom_boxplot(outlier.color = "red", alpha = .7) +
labs(title = "Masa corporal por especie",
x = NULL, y = "Masa (g)") +
scale_fill_brewer(palette = "Pastel1") +
theme_minimal(base_size = 12) +
theme(legend.position = "none")
- Los puntos rojos indican outliers según 1.5 × IQR.
- Eliminar la leyenda evita redundancia cuando el llenado replica el eje X.
5.1.4 Diagrama de dispersión con codificación doble
ggplot(penguins_min,
aes(x = bill_length_mm, y = body_mass_g,
colour = species, shape = sex)) +
geom_point(size = 2.5, alpha = .8) +
labs(title = "Longitud del pico vs. masa corporal",
x = "Longitud pico (mm)", y = "Masa (g)",
colour = "Especie", shape = "Sexo") +
scale_colour_brewer(palette = "Dark2") +
theme_light()
Este gráfico revela clusters claros por especie y, con shape, las diferencias
entre sexos sin recargar con facetas.
5.2 Buenas prácticas de estética y etiquetado
| Recomendación | Por qué importa | Ejemplo en ggplot2 |
|---|---|---|
| Títulos y subtítulos descriptivos | Contextualizan la lectura sin acudir al texto principal. | labs(title = "...", subtitle = "...") |
| Ejes con unidades claras | Evita ambigüedad (p. ej. g vs. kg). | scale_x_continuous(labels = scales::comma) |
| Paletas accesibles | Garantiza legibilidad para personas con daltonismo. | scale_fill_brewer(palette = "Set2") |
| Leyendas útiles, no redundantes | Reducen ruido visual. | theme(legend.position = "none") |
| Temas coherentes | Unifica el estilo (fondos, tipografías). | theme_minimal(base_size = 11) |
| Guardar en alta resolución | Evita pixelación en informes y presentaciones. | ggsave("plot.png", dpi = 300, width = 6, height = 4) |
Con estas prácticas, incluso un toy dataset luce profesional y comunica una historia clara. Recuerda: una buena gráfica no lo dice todo, pero impulsa a preguntar más —y ahí comienza la investigación.
6. De la pregunta a la mini-investigación
Todo el trabajo previo (elección del toy dataset, limpieza mínima, descriptivos y gráficos) debe
converger en una pregunta clara. Esa pregunta se convierte
en una hipótesis, y con la prueba estadística adecuada
podemos aceptarla o rechazarla con evidencia.
En esta sección se muestra el camino “exprés” para pasar del
“¿será que…?” al “los datos sugieren que…”.
6.1 Formulación de hipótesis simples
Hipótesis nula (H0)
Es la posición de partida: “no hay diferencia / no hay efecto / todo es azar”.
Hipótesis alternativa (HA)
Lo que esperamos encontrar: “sí hay diferencia / sí hay efecto”.
| Ejemplo de pregunta | H0 | HA |
|---|---|---|
| ¿La masa corporal media difiere entre pingüinos Adélie y Gentoo? | μAdélie = μGentoo | μAdélie ≠ μGentoo |
| ¿La preferencia por “Café” depende del género? | La proporción de fanáticos del café es igual en hombres y mujeres. | La proporción de fanáticos del café difiere entre géneros. |
Mantén las hipótesis específicas, medibles y binarias: o se rechaza H0 o no se rechaza (nunca “se acepta” H0; simplemente no hay evidencia suficiente en su contra).
6.2 Selección de pruebas básicas (t-test, chi-cuadrado “light”)
Con toy datasets bastan dos test clásicos — fáciles de ejecutar,
interpretar y reportar:
| Situación típica | Prueba recomendada | Función en R | Supuestos clave |
|---|---|---|---|
| Comparar medias de una variable numérica entre dos grupos. | t-test de dos muestras (Student o Welch). | t.test(x ~ grupo) | Independencia, aproximada normalidad y varianzas similares (o usar Welch). |
| Comparar frecuencias de dos variables categóricas (contingencia 2 × k). | Chi-cuadrado de independencia. | chisq.test(tabla) | Conteos esperados > 5 en ≥ 80 % de las celdas. |
6.2.1 t-test paso a paso (penguins)
library(dplyr)
library(palmerpenguins)
penguins_clean <- penguins |>
filter(!is.na(body_mass_g), !is.na(species)) |>
filter(species %in% c("Adelie", "Gentoo"))
with(penguins_clean, t.test(body_mass_g ~ species))
- R informa el estadístico t, grados de libertad, valor p y la diferencia de medias con intervalo de confianza 95 %.
- Si p < 0.05 ⇒ evidencia suficiente para
afirmar que las especies difieren en masa.
6.2.2 Chi-cuadrado exprés
tabla_bebida <- table(datos$genero, datos$bebida) chisq.test(tabla_bebida)
R entrega χ², df y valor p.
Si p < 0.05 ⇒ la preferencia de bebida está asociada al género.
Consejos “light” para toy datasets
- Siempre
na.omit()odrop_na()antes de la prueba. - Comprueba normalidad con un histograma rápido o
shapiro.test()(n ≤ 50). - Para varianzas dispares usa
t.test(..., var.equal = FALSE)(Welch). - Si algún conteo esperado < 5: agrupa categorías o usa
fisher.test(). - Reporta: estadístico, df, valor p, estimaciones (diferencia o razón) y
un IC 95 %. Ejemplo:
“t(81.4)=-4.23, p<.001; Δ x̄ = –102 g
[-150, -55], mayor masa en Gentoo”.
Con estos pasos tu mini-investigación ya tiene una pregunta acotada, una prueba apropiada y un resultado interpretable que enlaza con la historia que empezaste a contar mediante tablas y gráficos.
