# Creamos un data.table
dt <- data.table(x = 1:3, y = c("A", "B", "C"))
# Verificamos su herencia
class(dt)
#> [1] "data.table" "data.frame"
is.data.frame(dt)
#> [1] TRUE1 Introducción a data.table
1.1 La Historia de data.table
data.table nació en 2006 de la frustración de Matt Dowle con la velocidad de operaciones sobre data.frame para datasets grandes. Su visión era simple pero revolucionaria: ¿por qué conformarse con segundos cuando puedes tener milisegundos?
Hoy, más de 15 años después, data.table es la referencia mundial para manipulación de datos de alta performance en R, usado por empresas como Facebook, Netflix, y miles de científicos de datos alrededor del mundo.
1.2 ¿Qué hace a data.table especial?
1.2.1 1. Es un data.frame mejorado 🔧
Un data.table es un data.frame. Esto significa:
Esta compatibilidad es crucial: puedes usar un data.table en cualquier lugar que espere un data.frame sin problemas.
1.2.2 2. Modificación por referencia 🧠
La característica más distintiva de data.table es su capacidad de modificar objetos por referencia, sin crear copias.
# R base: crea una copia completa
df <- data.frame(x = 1:1000000, y = rnorm(1000000))
df$z <- df$x + df$y # ¡Copia completa en memoria!
# data.table: modificación in-place
dt <- data.table(x = 1:1000000, y = rnorm(1000000))
dt[, z := x + y] # ¡Sin copias! Modificación directa- Ventaja: Velocidad extrema y uso eficiente de memoria
- Cuidado: Los cambios son permanentes, no hay “deshacer” automático
- Solución: Usa
copy()cuando necesites preservar el original
1.2.3 3. Sintaxis consistente y potente ✍️
Todo en data.table se reduce a una estructura simple pero poderosa:
# La estructura universal de data.table
DT[i, j, by]
# i = filas (WHERE) - "¿Qué filas me interesan?"
# j = columnas (WHAT) - "¿Qué quiero hacer?"
# by = grupos (BY) - "¿Agrupado por qué?"1.3 Comparación: data.table vs. dplyr vs. R base
Veamos cómo se comparan las tres principales formas de manipular datos en R:
1.3.1 Tarea: Calcular la media de valor por categoria
# R base: verbose y lento
resultado_base <- aggregate(valor ~ categoria, data = datos, FUN = mean)
head(resultado_base)
#> categoria valor
#> 1 A 100.0053
#> 2 B 100.0914
#> 3 C 100.1848
#> 4 D 100.0232
#> 5 E 100.0879# dplyr: legible pero más lento para datos grandes
resultado_dplyr <- datos %>%
group_by(categoria) %>%
summarise(media_valor = mean(valor), .groups = 'drop')
head(resultado_dplyr)
#> # A tibble: 5 × 2
#> categoria media_valor
#> <chr> <dbl>
#> 1 A 100.
#> 2 B 100.
#> 3 C 100.
#> 4 D 100.
#> 5 E 100.# data.table: conciso y ultra rápido
resultado_dt <- datos_dt[, .(media_valor = mean(valor)), by = categoria]
head(resultado_dt)
#> categoria media_valor
#> <char> <num>
#> 1: C 100.1848
#> 2: B 100.0914
#> 3: D 100.0232
#> 4: A 100.0053
#> 5: E 100.08791.3.2 Comparación de Rendimiento
# Comparamos velocidad (solo con muestra para el ejemplo)
if(nrow(datos) > 10000) {
muestra <- slice_sample(datos, n = 10000)
muestra_dt <- as.data.table(muestra)
benchmark_resultado <- microbenchmark(
"R Base" = aggregate(valor ~ categoria, data = muestra, FUN = mean),
"dplyr" = muestra %>% group_by(categoria) %>% summarise(media = mean(valor), .groups = 'drop'),
"data.table" = muestra_dt[, .(media = mean(valor)), by = categoria],
times = 50
)
print(benchmark_resultado)
}
#> Unit: microseconds
#> expr min lq mean median uq max neval
#> R Base 2937.830 2993.684 3099.1619 3030.267 3085.305 4299.811 50
#> dplyr 2419.343 2545.087 2810.3213 2608.751 2823.386 6635.326 50
#> data.table 822.574 900.339 948.0281 931.908 968.456 1207.432 50En datasets grandes (millones de filas), data.table puede ser 10-100x más rápido que R base y 2-10x más rápido que dplyr. La diferencia se amplifica con operaciones más complejas.
1.4 Tu Primer data.table
1.4.1 Creación Básica
Existen varias formas de crear un data.table:
# 1. Desde cero
mi_dt <- data.table(
nombre = c("Ana", "Juan", "María", "Carlos"),
edad = c(25, 30, 28, 35),
ciudad = c("Madrid", "Barcelona", "Sevilla", "Valencia"),
salario = c(35000, 42000, 38000, 50000)
)
print(mi_dt)
#> nombre edad ciudad salario
#> <char> <num> <char> <num>
#> 1: Ana 25 Madrid 35000
#> 2: Juan 30 Barcelona 42000
#> 3: María 28 Sevilla 38000
#> 4: Carlos 35 Valencia 50000# 2. Convirtiendo un data.frame existente
iris_dt <- as.data.table(iris)
print(head(iris_dt))
#> Sepal.Length Sepal.Width Petal.Length Petal.Width Species
#> <num> <num> <num> <num> <fctr>
#> 1: 5.1 3.5 1.4 0.2 setosa
#> 2: 4.9 3.0 1.4 0.2 setosa
#> 3: 4.7 3.2 1.3 0.2 setosa
#> 4: 4.6 3.1 1.5 0.2 setosa
#> 5: 5.0 3.6 1.4 0.2 setosa
#> 6: 5.4 3.9 1.7 0.4 setosa# 3. Leyendo un archivo (súper rápido)
datos_csv <- fread("mi_archivo.csv")1.4.2 Explorando tu data.table
# Información básica
dim(iris_dt) # Dimensiones
#> [1] 150 5
names(iris_dt) # Nombres de columnas
#> [1] "Sepal.Length" "Sepal.Width" "Petal.Length" "Petal.Width" "Species"
str(iris_dt) # Estructura
#> Classes 'data.table' and 'data.frame': 150 obs. of 5 variables:
#> $ Sepal.Length: num 5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
#> $ Sepal.Width : num 3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
#> $ Petal.Length: num 1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
#> $ Petal.Width : num 0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
#> $ Species : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...
#> - attr(*, ".internal.selfref")=<externalptr># Estadísticas rápidas por grupo
iris_dt[, .N, by = Species] # Conteo por especie
#> Species N
#> <fctr> <int>
#> 1: setosa 50
#> 2: versicolor 50
#> 3: virginica 501.5 Configuración del Entorno
Para aprovechar al máximo data.table, configuremos nuestro entorno:
# 1. Configurar número de threads (para aprovechar múltiples núcleos)
setDTthreads(0) # 0 = usar todos los núcleos disponibles
getDTthreads() # Verificar configuración
#> [1] 4
# 2. Opciones de visualización
options(datatable.print.nrows = 10) # Mostrar 10 filas
options(datatable.print.class = TRUE) # Mostrar clases de columnas
# 3. Modo verboso para aprendizaje (opcional)
# options(datatable.verbose = TRUE) # Descomenta para debugsetDTthreads(0) utiliza todos los núcleos de CPU disponibles. En máquinas con muchos núcleos, esto puede acelerar operaciones hasta 4-8x. Para laptops o uso interactivo, considera setDTthreads(2) para mantener el sistema responsive.
1.6 Anatomía de la Sintaxis DT[i, j, by]
La sintaxis de data.table puede parecer intimidante al principio, pero es increíblemente lógica:
# Ejemplo con nuestros datos de empleados
print(mi_dt)
#> nombre edad ciudad salario
#> <char> <num> <char> <num>
#> 1: Ana 25 Madrid 35000
#> 2: Juan 30 Barcelona 42000
#> 3: María 28 Sevilla 38000
#> 4: Carlos 35 Valencia 50000
# i: ¿QUÉ FILAS? (filtrado)
mi_dt[edad > 28]
#> nombre edad ciudad salario
#> <char> <num> <char> <num>
#> 1: Juan 30 Barcelona 42000
#> 2: Carlos 35 Valencia 50000
# j: ¿QUÉ HACER? (selección/cálculo)
mi_dt[, .(nombre, salario)]
#> nombre salario
#> <char> <num>
#> 1: Ana 35000
#> 2: Juan 42000
#> 3: María 38000
#> 4: Carlos 50000
# by: ¿AGRUPADO POR QUÉ? (agrupación)
mi_dt[, mean(salario), by = ciudad]
#> ciudad V1
#> <char> <num>
#> 1: Madrid 35000
#> 2: Barcelona 42000
#> 3: Sevilla 38000
#> 4: Valencia 500001.7 Ejercicio Práctico: Tu Primera Experiencia
# 1. Filtrar filas
iris_grandes <- iris_dt[Sepal.Length > 6.0]
print(nrow(iris_grandes))
#> [1] 61
# 2. Media de Petal.Width por Species
medias_petal <- iris_dt[, .(media_petal_width = mean(Petal.Width)), by = Species]
print(medias_petal)
#> Species media_petal_width
#> <fctr> <num>
#> 1: setosa 0.246
#> 2: versicolor 1.326
#> 3: virginica 2.026
# 3. Seleccionar columnas que empiecen con "Petal"
columnas_petal <- iris_dt[, .SD, .SDcols = patterns("^Petal")]
print(head(columnas_petal))
#> Petal.Length Petal.Width
#> <num> <num>
#> 1: 1.4 0.2
#> 2: 1.4 0.2
#> 3: 1.3 0.2
#> 4: 1.5 0.2
#> 5: 1.4 0.2
#> 6: 1.7 0.4Explicación: - [Sepal.Length > 6.0] filtra filas por condición lógica - .(media_petal_width = mean(Petal.Width)) crea una nueva columna calculada - patterns("^Petal") usa regex para seleccionar columnas dinámicamente
1.8 Próximos Pasos
Has dado tus primeros pasos en el mundo de data.table. En el siguiente capítulo profundizaremos en cada componente de la sintaxis DT[i, j, by] y descubrirás el verdadero poder de esta herramienta.
1.8.1 Lo que viene
En el Capítulo 1 exploraremos: - Filtrado avanzado de filas (i) - Selección y transformación de columnas (j) - Agrupaciones poderosas (by) - Símbolos especiales como .N, .SD, etc.
data.table no es solo una herramienta más rápida - es una forma diferente de pensar sobre la manipulación de datos. La inversión inicial en aprender su sintaxis te pagará dividendos durante toda tu carrera como analista de datos.