# La estructura universal
DT[i, j, by]
↓ ↓ ↓
¿Dónde? ¿Qué hacer? ¿Agrupado por?
(filtros) (seleccionar/ (variables de
transformar) agrupación)2 La Sintaxis DT[i, j, by]
2.1 La Sintaxis DT[i, j, by]: El Corazón de data.table
Todo en data.table gira alrededor de esta estructura simple pero poderosa. Piensa en ella como una pregunta en tres partes que le haces a tus datos:
Cada componente es opcional, lo que hace la sintaxis extremadamente flexible.
2.2 El Componente i: Selección y Filtrado de Filas
2.2.1 1. Filtrado Básico por Condiciones
# Empleados con salario > 45000
empleados[salario > 45000]
#> id nombre departamento salario años_exp fecha_ingreso
#> <int> <char> <char> <num> <num> <Date>
#> 1: 4 Carlos IT 50000 8 2015-09-20
#> 2: 6 Pedro IT 55000 10 2013-04-12
#> 3: 8 Miguel Marketing 47000 7 2017-08-15
#> 4: 10 Jorge RRHH 48000 9 2014-12-01
#> 5: 12 Diego RRHH 52000 8 2016-01-25# Múltiples condiciones: IT con más de 5 años de experiencia
empleados[departamento == "IT" & años_exp > 5]
#> id nombre departamento salario años_exp fecha_ingreso
#> <int> <char> <char> <num> <num> <Date>
#> 1: 4 Carlos IT 50000 8 2015-09-20
#> 2: 5 Lucía IT 45000 6 2018-11-05
#> 3: 6 Pedro IT 55000 10 2013-04-12# Usando %in% para múltiples valores
empleados[departamento %in% c("Ventas", "Marketing")]
#> id nombre departamento salario años_exp fecha_ingreso
#> <int> <char> <char> <num> <num> <Date>
#> 1: 1 Ana Ventas 35000 2 2022-01-15
#> 2: 2 Juan Ventas 42000 5 2019-03-10
#> 3: 3 María Ventas 38000 3 2021-07-01
#> 4: 7 Isabel Marketing 40000 4 2020-02-28
#> 5: 8 Miguel Marketing 47000 7 2017-08-15
#> 6: 9 Carmen Marketing 41000 3 2021-10-032.2.2 2. Filtrado por Posición
# Primeras 3 filas
empleados[1:3]
#> id nombre departamento salario años_exp fecha_ingreso
#> <int> <char> <char> <num> <num> <Date>
#> 1: 1 Ana Ventas 35000 2 2022-01-15
#> 2: 2 Juan Ventas 42000 5 2019-03-10
#> 3: 3 María Ventas 38000 3 2021-07-01
# Última fila usando .N (número total de filas)
empleados[.N]
#> id nombre departamento salario años_exp fecha_ingreso
#> <int> <char> <char> <num> <num> <Date>
#> 1: 12 Diego RRHH 52000 8 2016-01-25
# Últimas 3 filas
empleados[(.N-2):.N]
#> id nombre departamento salario años_exp fecha_ingreso
#> <int> <char> <char> <num> <num> <Date>
#> 1: 10 Jorge RRHH 48000 9 2014-12-01
#> 2: 11 Laura RRHH 44000 5 2019-06-18
#> 3: 12 Diego RRHH 52000 8 2016-01-252.2.3 3. Filtrado por Orden
# Ordenar por salario (ascendente)
empleados[order(salario)]
#> id nombre departamento salario años_exp fecha_ingreso
#> <int> <char> <char> <num> <num> <Date>
#> 1: 1 Ana Ventas 35000 2 2022-01-15
#> 2: 3 María Ventas 38000 3 2021-07-01
#> 3: 7 Isabel Marketing 40000 4 2020-02-28
#> 4: 9 Carmen Marketing 41000 3 2021-10-03
#> 5: 2 Juan Ventas 42000 5 2019-03-10
#> ---
#> 8: 8 Miguel Marketing 47000 7 2017-08-15
#> 9: 10 Jorge RRHH 48000 9 2014-12-01
#> 10: 4 Carlos IT 50000 8 2015-09-20
#> 11: 12 Diego RRHH 52000 8 2016-01-25
#> 12: 6 Pedro IT 55000 10 2013-04-12
# Ordenar por salario descendente
empleados[order(-salario)]
#> id nombre departamento salario años_exp fecha_ingreso
#> <int> <char> <char> <num> <num> <Date>
#> 1: 6 Pedro IT 55000 10 2013-04-12
#> 2: 12 Diego RRHH 52000 8 2016-01-25
#> 3: 4 Carlos IT 50000 8 2015-09-20
#> 4: 10 Jorge RRHH 48000 9 2014-12-01
#> 5: 8 Miguel Marketing 47000 7 2017-08-15
#> ---
#> 8: 2 Juan Ventas 42000 5 2019-03-10
#> 9: 9 Carmen Marketing 41000 3 2021-10-03
#> 10: 7 Isabel Marketing 40000 4 2020-02-28
#> 11: 3 María Ventas 38000 3 2021-07-01
#> 12: 1 Ana Ventas 35000 2 2022-01-15
# Ordenar por múltiples columnas
empleados[order(departamento, -salario)]
#> id nombre departamento salario años_exp fecha_ingreso
#> <int> <char> <char> <num> <num> <Date>
#> 1: 6 Pedro IT 55000 10 2013-04-12
#> 2: 4 Carlos IT 50000 8 2015-09-20
#> 3: 5 Lucía IT 45000 6 2018-11-05
#> 4: 8 Miguel Marketing 47000 7 2017-08-15
#> 5: 9 Carmen Marketing 41000 3 2021-10-03
#> ---
#> 8: 10 Jorge RRHH 48000 9 2014-12-01
#> 9: 11 Laura RRHH 44000 5 2019-06-18
#> 10: 2 Juan Ventas 42000 5 2019-03-10
#> 11: 3 María Ventas 38000 3 2021-07-01
#> 12: 1 Ana Ventas 35000 2 2022-01-15data.table optimiza automáticamente muchas operaciones de filtrado. Para datasets enormes, considera usar setkey() para acelerar filtros repetitivos (lo veremos en capítulos posteriores).
2.3 El Componente j: El Poder de la Transformación
2.3.1 1. Selección Simple de Columnas
# Seleccionar una columna (devuelve vector)
empleados[, salario]
#> [1] 35000 42000 38000 50000 45000 55000 40000 47000 41000 48000 44000 52000
# Seleccionar una columna (devolver data.table)
empleados[, .(salario)]
#> salario
#> <num>
#> 1: 35000
#> 2: 42000
#> 3: 38000
#> 4: 50000
#> 5: 45000
#> ---
#> 8: 47000
#> 9: 41000
#> 10: 48000
#> 11: 44000
#> 12: 52000
# Múltiples columnas
empleados[, .(nombre, salario, departamento)]
#> nombre salario departamento
#> <char> <num> <char>
#> 1: Ana 35000 Ventas
#> 2: Juan 42000 Ventas
#> 3: María 38000 Ventas
#> 4: Carlos 50000 IT
#> 5: Lucía 45000 IT
#> ---
#> 8: Miguel 47000 Marketing
#> 9: Carmen 41000 Marketing
#> 10: Jorge 48000 RRHH
#> 11: Laura 44000 RRHH
#> 12: Diego 52000 RRHH2.3.2 2. Creación de Nuevas Columnas
# Crear columna calculada
empleados[, .(nombre, salario, salario_anual = salario * 12)]
#> nombre salario salario_anual
#> <char> <num> <num>
#> 1: Ana 35000 420000
#> 2: Juan 42000 504000
#> 3: María 38000 456000
#> 4: Carlos 50000 600000
#> 5: Lucía 45000 540000
#> ---
#> 8: Miguel 47000 564000
#> 9: Carmen 41000 492000
#> 10: Jorge 48000 576000
#> 11: Laura 44000 528000
#> 12: Diego 52000 624000
# Múltiples cálculos
empleados[, .(
nombre,
salario_mensual = salario,
salario_anual = salario * 12,
salario_por_año_exp = round(salario / años_exp, 0)
)]
#> nombre salario_mensual salario_anual salario_por_año_exp
#> <char> <num> <num> <num>
#> 1: Ana 35000 420000 17500
#> 2: Juan 42000 504000 8400
#> 3: María 38000 456000 12667
#> 4: Carlos 50000 600000 6250
#> 5: Lucía 45000 540000 7500
#> ---
#> 8: Miguel 47000 564000 6714
#> 9: Carmen 41000 492000 13667
#> 10: Jorge 48000 576000 5333
#> 11: Laura 44000 528000 8800
#> 12: Diego 52000 624000 65002.3.3 3. Funciones de Agregación
# Estadísticas básicas
empleados[, .(
salario_promedio = mean(salario),
salario_mediana = median(salario),
salario_max = max(salario),
salario_min = min(salario),
total_empleados = .N
)]
#> salario_promedio salario_mediana salario_max salario_min total_empleados
#> <num> <num> <num> <num> <int>
#> 1: 44750 44500 55000 35000 122.3.4 4. El Símbolo Especial .N
.N es una variable especial que representa el número de filas en el grupo actual:
# Contar total de filas
empleados[, .N]
#> [1] 12
# Contar empleados por departamento
empleados[, .N, by = departamento]
#> departamento N
#> <char> <int>
#> 1: Ventas 3
#> 2: IT 3
#> 3: Marketing 3
#> 4: RRHH 3
# Usar .N en cálculos
empleados[, .(
empleados_total = .N,
salario_promedio = mean(salario)
)]
#> empleados_total salario_promedio
#> <int> <num>
#> 1: 12 447502.4 El Componente by: Agrupaciones Poderosas
2.4.1 1. Agrupación Simple
# Estadísticas por departamento
empleados[, .(
empleados = .N,
salario_promedio = round(mean(salario), 0),
salario_total = sum(salario)
), by = departamento]
#> departamento empleados salario_promedio salario_total
#> <char> <int> <num> <num>
#> 1: Ventas 3 38333 115000
#> 2: IT 3 50000 150000
#> 3: Marketing 3 42667 128000
#> 4: RRHH 3 48000 1440002.4.2 2. Agrupación por Múltiples Variables
# Crear categorías de experiencia para el ejemplo
empleados[, categoria_exp := ifelse(años_exp <= 5, "Junior", "Senior")]
# Agrupar por departamento y categoría de experiencia
empleados[, .(
empleados = .N,
salario_promedio = round(mean(salario), 0)
), by = .(departamento, categoria_exp)]
#> departamento categoria_exp empleados salario_promedio
#> <char> <char> <int> <num>
#> 1: Ventas Junior 3 38333
#> 2: IT Senior 3 50000
#> 3: Marketing Junior 2 40500
#> 4: Marketing Senior 1 47000
#> 5: RRHH Senior 2 50000
#> 6: RRHH Junior 1 440002.4.3 3. Agrupación con Expresiones
# Agrupar por rangos de salario calculados sobre la marcha
empleados[, .(
empleados = .N,
salario_promedio = round(mean(salario), 0)
), by = .(rango_salario = ifelse(salario > 45000, "Alto", "Medio-Bajo"))]
#> rango_salario empleados salario_promedio
#> <char> <int> <num>
#> 1: Medio-Bajo 7 40714
#> 2: Alto 5 504002.5 Combinando los Tres Componentes
La verdadera potencia surge cuando combinas i, j, y by:
# Filtrar empleados con > 4 años exp, calcular stats por departamento
empleados[años_exp > 4, .(
empleados_experimentados = .N,
salario_promedio = round(mean(salario), 0),
años_exp_promedio = round(mean(años_exp), 1)
), by = departamento]
#> departamento empleados_experimentados salario_promedio años_exp_promedio
#> <char> <int> <num> <num>
#> 1: Ventas 1 42000 5.0
#> 2: IT 3 50000 8.0
#> 3: Marketing 1 47000 7.0
#> 4: RRHH 3 48000 7.32.6 Ejercicios Prácticos
# 1. Filtrado avanzado
empleados_filtrados <- empleados[
departamento %in% c("Ventas", "IT") & salario > 40000
]
print(empleados_filtrados)
#> id nombre departamento salario años_exp fecha_ingreso categoria_exp
#> <int> <char> <char> <num> <num> <Date> <char>
#> 1: 2 Juan Ventas 42000 5 2019-03-10 Junior
#> 2: 4 Carlos IT 50000 8 2015-09-20 Senior
#> 3: 5 Lucía IT 45000 6 2018-11-05 Senior
#> 4: 6 Pedro IT 55000 10 2013-04-12 Senior
# 2. Agregación por departamento
stats_dept <- empleados[, .(
total_empleados = .N,
salario_promedio = round(mean(salario), 0),
años_exp_maximo = max(años_exp)
), by = departamento]
print(stats_dept)
#> departamento total_empleados salario_promedio años_exp_maximo
#> <char> <int> <num> <num>
#> 1: Ventas 3 38333 5
#> 2: IT 3 50000 10
#> 3: Marketing 3 42667 7
#> 4: RRHH 3 48000 9
# 3. Empleados por encima del promedio de experiencia en su departamento
empleados_promedio_exp <- empleados[, exp_promedio_dept := mean(años_exp), by = departamento][
años_exp > exp_promedio_dept,
.(nombre, departamento, años_exp, exp_promedio_dept)
]
print(empleados_promedio_exp)
#> nombre departamento años_exp exp_promedio_dept
#> <char> <char> <num> <num>
#> 1: Juan Ventas 5 3.333333
#> 2: Pedro IT 10 8.000000
#> 3: Miguel Marketing 7 4.666667
#> 4: Jorge RRHH 9 7.333333
#> 5: Diego RRHH 8 7.3333332.7 Patrones Comunes y Mejores Prácticas
2.7.1 1. Piensa en SQL
Si conoces SQL, data.table te resultará familiar:
# SQL: SELECT departamento, AVG(salario) FROM empleados WHERE salario > 40000 GROUP BY departamento
# data.table:
empleados[salario > 40000, .(salario_promedio = mean(salario)), by = departamento]2.7.2 2. Usa .() para Múltiples Columnas
.() es tu amigo para selecciones y cálculos múltiples:
# ✅ Correcto: múltiples columnas con nombres descriptivos
empleados[, .(
empleado = nombre,
dept = departamento,
sueldo = salario,
experiencia = años_exp
)]
#> empleado dept sueldo experiencia
#> <char> <char> <num> <num>
#> 1: Ana Ventas 35000 2
#> 2: Juan Ventas 42000 5
#> 3: María Ventas 38000 3
#> 4: Carlos IT 50000 8
#> 5: Lucía IT 45000 6
#> ---
#> 8: Miguel Marketing 47000 7
#> 9: Carmen Marketing 41000 3
#> 10: Jorge RRHH 48000 9
#> 11: Laura RRHH 44000 5
#> 12: Diego RRHH 52000 82.7.3 3. Combina Operaciones de Forma Legible
# Ejemplo complejo pero legible
analisis_empleados <- empleados[
años_exp > 3, # i: filtrar por experiencia
.( # j: calcular métricas
empleados = .N,
salario_promedio = round(mean(salario), 0),
experiencia_total = sum(años_exp),
salario_max = max(salario)
),
by = departamento # by: agrupar por departamento
][order(-salario_promedio)] # ordenar por salario desc
print(analisis_empleados)
#> departamento empleados salario_promedio experiencia_total salario_max
#> <char> <int> <num> <num> <num>
#> 1: IT 3 50000 24 55000
#> 2: RRHH 3 48000 22 52000
#> 3: Marketing 2 43500 11 47000
#> 4: Ventas 1 42000 5 420002.8 Comparación de Performance
Veamos cómo la sintaxis data.table se compara con alternativas:
# Preparar datos para comparación
library(microbenchmark)
ventas_sample <- ventas[sample(.N, 10000)] # Muestra para benchmark
# Comparar diferentes aproximaciones
benchmark_sintaxis <- microbenchmark(
"data.table" = ventas_sample[precio > 100, .(avg_precio = mean(precio)), by = categoria],
"base R" = aggregate(precio ~ categoria, ventas_sample[ventas_sample$precio > 100, ], mean),
times = 20
)
print(benchmark_sintaxis)
#> Unit: milliseconds
#> expr min lq mean median uq max neval
#> data.table 1.012829 1.175773 1.260604 1.238780 1.365142 1.658243 20
#> base R 3.681327 3.750626 4.768993 5.181716 5.331475 7.961470 202.9 Próximo Capítulo: Símbolos Especiales
En el siguiente capítulo profundizaremos en: - .SD: El subset de datos por grupo - .SDcols: Control granular de columnas - .I y .GRP: Índices y identificadores de grupo - Casos de uso avanzados de estos símbolos
DT[i, j, by]es la sintaxis universal - domínala y dominarásdata.tableifiltra filas - usa condiciones lógicas, posiciones, u ordenamientojselecciona/calcula - usa.()para múltiples columnas con nombresbyagrupa datos - puede usar múltiples variables o expresiones.Ncuenta filas - funciona en contexto general o por grupo- Combina libremente - la flexibilidad es el superpoder de
data.table
Con estos fundamentos sólidos de la sintaxis DT[i, j, by], estás listo para explorar los símbolos especiales que hacen de data.table una herramienta verdaderamente poderosa.