Unidad 3 - Transformación de Datos con dplyr
Introducción al Business Analytics · Semana 4 · 06278-ECO
1 Objetivo de la semana
Esta semana aprenderemos a transformar datos crudos en información lista para tomar decisiones. El foco NO está en “dominar dplyr”, sino en responder preguntas de negocio mediante filtros, cálculos, agrupaciones y resúmenes.
¿Por qué necesitamos transformar datos?
Los datos raramente vienen en el formato que necesitamos para responder preguntas. Casi siempre debemos:
- Filtrar: “Solo quiero ver las ventas de 2023”
- Calcular: “¿Cuál es el margen unitario de cada producto?”
- Agrupar: “¿Cuánto vendió cada región?”
- Resumir: “¿Cuál es el promedio de ventas por vendedor?”
dplyr es una “gramática” que nos permite hacer estas transformaciones con verbos claros. Esta semana aprenderemos cada verbo por separado. Al final de la clase los combinaremos.
1.1 Lo que aprenderemos esta semana
1. Transformaciones básicas — un verbo a la vez
- Seleccionar columnas con
select() - Filtrar filas con
filter() - Crear variables calculadas con
mutate() - Obtener resúmenes globales con
summarise()
2. Descriptivas por grupo
- Agrupar datos con
group_by() - Calcular KPIs por segmento con
group_by() + summarise()
Conexión con el flujo analítico
Ya entendemos qué es el proceso analítico (Semana 02) y cómo funciona R como entorno de trabajo (Semana 03). Ahora aprendemos a preparar tablas que respondan preguntas específicas antes de visualizarlas (Semana 05).
Esta semana trabajamos con datos “ideales” (sin valores faltantes ni errores). La limpieza la veremos en la Semana 06.
2 Introducción a dplyr
2.1 ¿Qué es dplyr?
dplyr es un paquete de R diseñado para manipular data frames de manera intuitiva. Es parte del tidyverse: un ecosistema de paquetes con una filosofía común de código claro y legible.
La idea central: verbos para acciones
En R base podemos filtrar filas así:
ventas[ventas$region == "Norte", ]Con dplyr, la misma operación se escribe así:
filter(ventas, region == "Norte")dplyr reemplaza la notación de corchetes por verbos que describen exactamente qué queremos hacer. Cada verbo hace una sola cosa, y tiene un nombre que lo dice claramente.
2.2 Instalación y carga
La función install.packages() descarga el paquete en tu computador — solo se hace una vez, igual que instalas una aplicación. library() lo activa para la sesión actual — esto sí hay que hacerlo cada vez que abres RStudio, igual que abres la app cada vez que la necesitas.
Regla práctica: install.packages() se corre una sola vez directo en la Consola. Nunca debe quedar activo en el script. Si quieres conservarla como referencia, déjala comentada: así evitas reinstalaciones innecesarias y, si compartes el script, otras personas no reinstalarán paquetes que ya tienen.
2.3 Dataset de ejemplo: ventas de una tienda
Usaremos este data frame durante toda la clase. Tiene 9 transacciones y 6 columnas: trimestre, producto, categoría, precio unitario, cantidad vendida y región del vendedor.
En el entorno de WebR (este documento), el dataset ya viene precargado: no necesitas leerlo desde ningún archivo. Basta con ejecutar el segundo chunk para inspeccionarlo.
Si trabajas desde RStudio, descomenta la línea read.csv(...) para cargar el dataset directamente desde internet.
3 Transformaciones básicas
La lógica de trabajo es siempre la misma:
resultado <- verbo(datos, argumentos)
Aplicamos un verbo al dataset y guardamos el resultado en un objeto nuevo. El dataset original no se modifica.
3.1 select(): Seleccionar columnas
La función select() devuelve solo las columnas que le pedimos.
¿Qué pasó aquí?
ventassigue intacto en el Environment (con 6 columnas)ventas_redes un objeto nuevo con solo 3 columnasselect()no modifica el original, crea una copia reducida
Puedes verificarlo ejecutando ncol(ventas) y ncol(ventas_red) por separado.
También puedes excluir columnas con el operador -. Útil cuando es más fácil decir qué NO quieres:
Si en algún momento trabajas con datasets de muchas columnas, select() tiene funciones auxiliares como starts_with() y ends_with() que permiten seleccionar columnas por patrones en sus nombres.
3.2 rename(): Renombrar columnas
La función rename() cambia el nombre de una o más columnas sin modificar su contenido. La sintaxis es nuevo_nombre = nombre_viejo:
¿Cuándo renombrar? Cuando los nombres originales no son descriptivos (x1, var2), contienen espacios o caracteres especiales, o quieres estandarizar nomenclatura antes de combinar datasets.
Buena práctica: minúsculas y guion bajo — precio_unitario, no Precio Unitario.
3.3 filter(): Filtrar filas
La función filter() devuelve solo las filas que cumplen una condición. Las demás se descartan.
3.3.1 Filtros simples
3.3.2 Operadores de comparación
La condición dentro de filter() siempre usa un operador de comparación. Estos son los más comunes:
| Operador | Significado | Ejemplo |
|---|---|---|
== |
Igual a | region == "Norte" |
!= |
Diferente de | region != "Sur" |
> |
Mayor que | precio > 100 |
< |
Menor que | precio < 500 |
>= |
Mayor o igual | cantidad >= 5 |
<= |
Menor o igual | precio <= 300 |
%in% |
Pertenece al conjunto | region %in% c("Norte","Sur") |
Error común: confundir = con ==
filter(ventas, region = "Norte") # Error — asigna en lugar de comparar
filter(ventas, region == "Norte") # Correcto — comparaDentro de filter(), siempre dos signos igual para comparar.
3.3.3 Filtros con múltiples condiciones
Cuando la pregunta exige que se cumplan dos condiciones al mismo tiempo, usamos &:
Cuando basta con que se cumpla al menos una, usamos |:
3.3.4 Mini-ejercicio
Escribe el código para responder cada pregunta. Si obtienes el resultado esperado, vas bien.
3.4 arrange(): Ordenar filas
Filtrar y calcular nos da los datos correctos, pero no siempre en el orden más útil para leerlos o comunicarlos. arrange() reorganiza las filas de un data frame según los valores de una o más columnas. Es especialmente útil como paso final antes de mostrar una tabla: un ranking de productos por ingreso, los clientes con mayor deuda primero, o las transacciones más recientes arriba son todos casos donde el orden importa tanto como el contenido.
Cómo leer el último ejemplo: “Ordena primero por región (A→Z). Dentro de cada región, ordena por precio de mayor a menor.”
3.5 mutate(): Crear nuevas variables
La función mutate() añade columnas calculadas al dataset. Las columnas originales se conservan.
3.5.1 Cálculos simples
mutate() mantiene el número de filas
mutate() siempre devuelve un data frame con el mismo número de filas que el original. Solo añade (o reemplaza) columnas. Cada fila recibe su propio valor calculado.
Más adelante veremos summarise(), que hace lo opuesto: colapsa todas las filas en un resumen.
3.5.2 Clasificar con ifelse()
Cómo leer esto: “Para cada fila, si el precio es mayor a 100 escribe "A", si no escribe "B".”
Cuando necesitas tres o más categorías, ifelse() anidado se vuelve difícil de leer. Para esos casos existe case_when() — explóralo en la documentación de dplyr cuando lo necesites.
3.5.3 Mini-ejercicio
Con el dataset ventas, crea una nueva columna tipo_venta que clasifique cada transacción así:
"Alta"si el ingreso (precio * cantidad) supera 500"Baja"en cualquier otro caso
Guarda el resultado en ventas_tipo e imprímelo.
Pista
Necesitas calcular el ingreso dentro de la condición: precio * cantidad > 500. No es necesario crear la columna ingreso primero — mutate() puede evaluarla directamente.
4 Resúmenes globales
4.1 summarise(): Colapsar datos en un resumen
La lógica de trabajo sigue siendo la misma:
resultado <- verbo(datos, argumentos)
summarise() es diferente a todos los verbos que hemos visto hasta ahora. select(), mutate() y arrange() devuelven un data frame con el mismo número de filas que el original. summarise() hace algo distinto: colapsa todas las filas en una sola, calculando una estadística que representa al grupo completo. Pensemos en ello así: si mutate() añade una columna nueva a cada fila, summarise() toma todas las filas y las “aplasta” en un único número.
4.1.1 Un solo estadístico
La forma más simple: pedirle a summarise() un único valor.
Qué pasó: el data frame ventas tenía 9 filas. summarise() las leyó todas, calculó el promedio de la columna precio, y devolvió una sola fila con ese resultado. El nombre precio_p es el que nosotros elegimos para la columna de salida.
4.1.2 Varios estadísticos a la vez
Podemos pedir múltiples resúmenes en una sola llamada. Cada uno se convierte en una columna del resultado:
Qué pasó: el mismo proceso de antes, pero ahora summarise() calculó cuatro estadísticos simultáneamente. El resultado sigue siendo una sola fila, pero ahora tiene cuatro columnas —una por cada estadístico que pedimos. La función especial n() no recibe ninguna columna como argumento: simplemente cuenta cuántas filas había en el dataset antes de colapsar.
Funciones de resumen más comunes
Tendencia central: mean() (promedio), median() (mediana)
Dispersión: sd() (desviación estándar), IQR() (rango intercuartílico)
Extremos: min(), max()
Conteos: n() (número de filas), n_distinct() (valores únicos), sum()
4.1.3 Mini-ejercicio
Usando filter(), mutate() y summarise(), calcula el ingreso total y el ticket promedio de las transacciones de la categoría Software. Guarda el resultado en resumen_soft e imprímelo.
Pista
ingreso no existe en el dataset original — hay que crearla con mutate() antes de poder usarla en summarise(). Si ves un error que dice object 'ingreso' not found, significa que saltaste el Paso 2.
Resultado esperado: una sola fila con ingreso_total = 1710, ticket_promedio = 427.5 y n_ventas = 4.
5 Descriptivas por grupo
5.1 ¿Qué significa agrupar?
summarise() sin agrupación produce una sola fila para todo el dataset. Pero casi siempre queremos comparar segmentos:
- Ventas por región
- Ticket promedio por vendedor
- Ingreso por categoría
Para eso usamos group_by() antes de summarise().
Concepto de granularidad
| Operación | Resultado |
|---|---|
summarise() sin grupo |
1 fila — resumen de todo |
group_by(region) + summarise() |
1 fila por cada región |
group_by(region, categoria) + summarise() |
1 fila por cada combinación región × categoría |
La granularidad define a qué nivel hacemos el análisis.
5.2 group_by() + summarise(): el dúo central
group_by() marca el dataset como agrupado. summarise() calcula los resúmenes dentro de cada grupo:
Cómo leer el resultado: Cada fila es una región. Podemos ver de un vistazo cuánto generó cada una y cuántas transacciones tuvo.
¿Qué hace group_by() exactamente?
group_by() no cambia el data frame visualmente. Lo que hace es marcar el objeto como “agrupado”. Puedes verificarlo ejecutando el objeto después de agrupar: verás # Groups: region [3] encima de la tabla.
Cuando el siguiente paso es summarise(), R calcula las estadísticas por separado para cada grupo.
5.3 Múltiples KPIs por grupo
Hasta ahora calculamos uno o dos estadísticos por grupo. En la práctica, casi siempre necesitamos varios KPIs a la vez para tener una visión completa del desempeño de cada segmento.
Qué pasó: cada región tiene ahora tres métricas. Podemos compararlas de un vistazo: cuál generó más ingresos, cuál tiene el ticket promedio más alto, cuál es más activa. El argumento .groups = "drop" desagrupa el resultado automáticamente — sin esto, el objeto quedaría marcado como agrupado y podría generar resultados inesperados en pasos posteriores.
De tabla a decisión
Esta tabla es el punto de partida para decisiones concretas:
- Invertir más presupuesto de marketing en la región de mayor ingreso
- Capacitar vendedores en regiones con ticket promedio bajo
- Redistribuir esfuerzos según volumen de transacciones
La analítica descriptiva diagnostica; la decisión la toma el negocio.
5.4 Agrupar por múltiples variables
A veces necesitamos mayor granularidad. Además de comparar regiones, queremos ver qué pasa dentro de cada región según otra dimensión: categoría de producto, trimestre, canal de venta, etc.
Cuando agrupamos por dos variables, obtenemos una fila por cada combinación única de esas variables.
Qué pasó: ahora cada fila representa una combinación región × categoría. Por ejemplo, podemos ver cuánto generó Hardware en la región Norte, cuánto Software en el Sur, etc. Esto nos ayuda a identificar qué categoría domina en cada zona geográfica.
Pregunta de análisis: ¿En qué regiones deberíamos promocionar más Software? Busca las regiones donde Software tiene bajo ingreso_total o bajo n_trans.
5.5 count(): atajo para contar frecuencias
Cuando solo necesitamos saber cuántas filas hay por grupo, escribir group_by() + summarise(n = n()) es innecesariamente largo. count() hace exactamente eso en una sola función.
Equivalente largo:
ventas_agr <- group_by(ventas, region)
conteo <- summarise(ventas_agr, n = n(), .groups = "drop")Si además queremos ordenar de mayor a menor, usamos sort = TRUE:
Cuándo usar count(): cuando solo te interesa la frecuencia. Si necesitas calcular otras métricas (promedios, totales, etc.), usa group_by() + summarise().
5.6 distinct() y n_distinct(): explorar categorías
A veces no queremos resumir datos, solo queremos saber qué valores únicos tiene una variable.
5.6.1 distinct(): mostrar valores únicos
Para qué sirve: exploración rápida del dataset. Antes de filtrar o agrupar, es útil ver qué valores existen.
5.6.2 n_distinct(): contar valores únicos
Cuando lo que necesitamos es el número de valores únicos (no verlos), usamos n_distinct() dentro de summarise():
Qué pasó: cada región tiene su conteo de productos únicos. Esto nos dice qué tan diversificado está el catálogo en cada zona.
distinct() vs. n_distinct()
distinct(ventas, region)→ tabla con los valores únicos de regiónn_distinct(region)dentro desummarise()→ un número (cuántos valores únicos)
Usa distinct() para ver, n_distinct() para contar.
5.7 Mini-ejercicio final
Usando lo aprendido, responde:
P1. ¿Cuántos productos distintos se vendieron en Q2?
P2. ¿Cuál es el ingreso total por trimestre? Ordena de mayor a menor.
P3. ¿Cuál es el ticket promedio (ingreso / n_trans) de Hardware vs. Software?
Pistas
- P1:
filter()+n_distinct()dentro desummarise() - P2:
mutate()→group_by(trimestre)→summarise()→arrange() - P3:
mutate()→group_by(categoria)→summarise()con promedio de ingreso
6 Working directory y organización
6.1 ¿Qué es el working directory?
El working directory (directorio de trabajo) es la carpeta en la que R está “parado” en este momento. Cuando le pides leer o guardar un archivo sin especificar la ruta completa, R busca en esta carpeta.
¿Por qué importa? Si intentas leer "ventas.csv" y R no encuentra el archivo, probablemente es porque el archivo está en otra carpeta y R está buscando en el working directory equivocado.
6.2 RStudio Projects: la mejor práctica
En lugar de cambiar el working directory manualmente con setwd() (lo cual rompe la reproducibilidad), usa RStudio Projects.
Ventajas de RStudio Projects
✓ R automáticamente establece el working directory en la carpeta del proyecto
✓ Cualquiera puede abrir el proyecto y todo funciona sin modificar rutas
✓ Cada análisis vive en su propia carpeta organizada
✓ R recuerda el historial y los objetos de la última sesión
Cómo crear uno:
- File → New Project…
- New Directory → New Project
- Dale un nombre descriptivo (ej:
analisis_ventas_2025) - R crea una carpeta con un archivo
.Rproj
Cómo usarlo: abre siempre el proyecto haciendo doble clic en el archivo .Rproj, no abriendo RStudio directamente.
6.3 Estructura de carpetas recomendada
analisis_ventas_2025/
├── analisis_ventas_2025.Rproj # archivo del proyecto
├── data/ # datos crudos (NUNCA modificar)
│ └── ventas.csv
├── scripts/ # código R
│ ├── 01_limpieza.R
│ └── 02_analisis.R
└── outputs/ # resultados generados
├── kpis_region.csv
└── grafico_ventas.png
Regla de oro: el contenido de data/ es intocable. Si necesitas modificar datos, hazlo en el script y guarda el resultado en outputs/.
6.4 Guardar una tabla como CSV
Una vez que tenemos nuestra tabla final de KPIs, podemos exportarla para compartirla o usarla en otras herramientas (Excel, Power BI, etc.):
## Guardar tabla de KPIs en la carpeta outputs
write.csv(kpis_region, "outputs/kpis_region.csv", row.names = FALSE)Nota: row.names = FALSE evita que R agregue una columna extra con números de fila.
7 Errores comunes y cómo evitarlos
Estos son los 5 errores más frecuentes al aprender dplyr. Si los reconoces y evitas, ahorrarás mucho tiempo de debugging.
7.1 1. No guardar el resultado
## INCORRECTO: se muestra en la consola pero no se guarda
filter(ventas, region == "Norte")
## CORRECTO: asignar a un objeto
ventas_norte <- filter(ventas, region == "Norte")Por qué pasa: olvidamos que R no modifica objetos automáticamente. Cada transformación debe guardarse explícitamente con <-.
7.2 2. Confundir = con ==
## INCORRECTO
filter(ventas, region = "Norte") # Error: unexpected '='
## CORRECTO
filter(ventas, region == "Norte")Por qué pasa: en matemáticas usamos = para igualdades. En R, = asigna valores (como <-) y == compara. Dentro de filter(), siempre ==.
7.3 3. Usar una variable que no existe todavía
## INCORRECTO: ingreso no existe en ventas
resumen_mal <- summarise(ventas, total = sum(ingreso))
# Error: object 'ingreso' not found
## CORRECTO: crear la variable primero
ventas_ing <- mutate(ventas, ingreso = precio * cantidad)
resumen_ok <- summarise(ventas_ing, total = sum(ingreso))Por qué pasa: mutate() crea columnas nuevas. Si queremos usarlas en pasos posteriores, primero hay que crearlas y guardar el resultado.
7.4 4. Olvidar que group_by() no hace nada solo
Para qué sirve: group_by() no colapsa datos ni calcula nada. Solo prepara el terreno. El cálculo lo hace summarise():
7.5 5. Confundir mutate() con summarise()
Este es el error conceptual más común. Ambas funciones crean columnas, pero funcionan de forma radicalmente distinta.
Regla mnemotécnica
mutate()→ Mantiene el número de filas. Añade una columna calculada para cada fila.summarise()→ Sintetiza. Colapsa todas las filas en un resumen.
¿Cuándo usar cuál?
- Quieres calcular algo para cada transacción (margen, descuento, categoría)? →
mutate() - Quieres un total, promedio o conteo global? →
summarise()
Ejemplo:
## mutate: 9 filas → 9 filas (con nueva columna)
ventas_ing <- mutate(ventas, ingreso = precio * cantidad)
## summarise: 9 filas → 1 fila (resumen global)
resumen <- summarise(ventas_ing, total = sum(ingreso))8 Checklist de salida
Al terminar esta semana, debes poder:
9 Preguntas de comprensión
9.1 1. Interpretación de output
Ejecuta el código y responde:
Pregunta: ¿Qué representa cada fila? ¿Cuántas filas esperabas y por qué?
9.2 2. Orden de operaciones
¿Cuál es la diferencia entre estos dos bloques?
Respuesta esperada:
- Bloque A: filtra transacciones con precio > 100 primero, luego cuenta por región.
- Bloque B: cuenta todas las transacciones por región primero, luego filtra regiones con más de 2 transacciones.
Son preguntas distintas con resultados distintos.
9.3 3. Aplicación práctica
Tienes un dataset de clientes con columnas cliente_id, compras_totales, region.
¿Cómo identificarías los 5 mejores clientes de cada región por monto de compras?
10 Próxima semana: Visualización con ggplot2
En la Semana 05 aprenderemos a visualizar las tablas de KPIs que construimos esta semana usando ggplot2:
- Gráficos de barras para comparar regiones y productos
- Gráficos de líneas para evolución por trimestre
- Gráficos de dispersión para relaciones entre variables
La tabla kpis_region que construimos hoy será la entrada directa para los primeros gráficos de la próxima semana. Todo lo que aprendiste esta semana sobre transformar datos es la base para comunicar resultados visualmente.