La sintaxis de data.table para la combinación izquierda(una columna) que prefiera
(1)
¿Cómo debo empezar a pensar qué sintaxis prefiero?
Mi criterio es la eficiencia (este es el número uno) y también la legibilidad / mantenibilidad.
Esta
A <- B[A, on = .(id)] # wow such. concision
O eso
A[B, on = .(id), comment := i.comment]
O incluso (como sugiere PoGibas):
A <- merge(A, B, all.x = TRUE)
Para completar, entonces una forma más básica es usar
match()
:
A[, comment := B[chmatch(A[["id"]], id), comment]]
Datos de ejemplo:
library(data.table)
A <- data.table(id = letters[1:10], amount = rnorm(10)^2)
B <- data.table(id = c("c", "d", "e"), comment = c("big", "slow", "nice"))
Prefiero el lenguaje "update join" por eficiencia y facilidad de mantenimiento: **
DT[WHERE, v := FROM[.SD, on=, x.v]]
Es una extensión de lo que se muestra en la
vignette("datatable-reference-semantics")
en "Actualizar algunas filas de columnas por referencia -
subasignar
por referencia".
Una vez que haya una viñeta disponible en las combinaciones, eso también debería ser una buena referencia.
Esto es eficiente ya que solo usa las filas seleccionadas por
WHERE
y modifica o agrega la columna en el lugar, en lugar de hacer una nueva tabla como la combinación izquierda más concisa
FROM[DT, on=]
.
Hace que mi código sea más legible ya que puedo ver fácilmente que el punto de la unión es agregar la columna
v
;
y no tengo que pensar en la jerga "izquierda" / "derecha" de SQL o si el número de filas se conserva después de la unión.
Es útil para el mantenimiento del código, ya que si más adelante quiero saber cómo
DT
obtuvo una columna llamada
v
, puedo buscar en mi código
v :=
, mientras que
FROM[DT, on=]
oculta qué nuevas columnas se están agregando.
Además, permite la condición
WHERE
, mientras que la unión izquierda no lo hace.
Esto puede ser útil, por ejemplo, si se
usa
FROM
para "llenar" NA en una columna existente
v
.
En comparación con el otro enfoque de combinación de actualización
DT[FROM, on=, v := iv]
, se me ocurren dos ventajas.
Primero está la opción de usar la cláusula
WHERE
, y la segunda es la transparencia a través de las advertencias cuando hay problemas con la unión, como coincidencias duplicadas en
FROM
condicional en las reglas
on=
.
Aquí hay una ilustración que extiende el ejemplo del OP:
library(data.table)
A <- data.table(id = letters[1:10], amount = rnorm(10)^2)
B2 <- data.table(
id = c("c", "d", "e", "e"),
ord = 1:4,
comment = c("big", "slow", "nice", "nooice")
)
# left-joiny update
A[B2, on=.(id), comment := i.comment, verbose=TRUE]
# Calculated ad hoc index in 0.000s elapsed (0.000s cpu)
# Starting bmerge ...done in 0.000s elapsed (0.000s cpu)
# Detected that j uses these columns: comment,i.comment
# Assigning to 4 row subset of 10 rows
# my preferred update
A[, comment2 := B2[A, on=.(id), x.comment]]
# Warning message:
# In `[.data.table`(A, , `:=`(comment2, B2[A, on = .(id), x.comment])) :
# Supplied 11 items to be assigned to 10 items of column ''comment2'' (1 unused)
id amount comment comment2
1: a 0.20000990 <NA> <NA>
2: b 1.42146573 <NA> <NA>
3: c 0.73047544 big big
4: d 0.04128676 slow slow
5: e 0.82195377 nooice nice
6: f 0.39013550 <NA> nooice
7: g 0.27019768 <NA> <NA>
8: h 0.36017876 <NA> <NA>
9: i 1.81865721 <NA> <NA>
10: j 4.86711754 <NA> <NA>
En la actualización con sabor a la izquierda, obtiene silenciosamente el valor final del
comment
, aunque hay dos coincidencias para
id == "e"
;
mientras está en la otra actualización, recibe un mensaje de advertencia útil (actualizado a
un error en una versión futura
).
Incluso activar
verbose=TRUE
con el enfoque de unión izquierda no es informativo; dice que hay cuatro filas que se están actualizando, pero no dice que una fila se esté actualizando dos veces.
Encuentro que este enfoque funciona mejor cuando mis datos se organizan en un conjunto de tablas ordenadas / relacionales. Una buena referencia al respecto es el artículo de Hadley Wickham .
** En este idioma, la parte
on=
debe completarse con los nombres y las reglas de la columna de unión, como
on=.(id)
o
on=.(from_date >= dt_date)
.
Se pueden pasar otras reglas de unión con
roll=
,
mult=
y
nomatch=
.
Ver
?data.table
para más detalles.
Gracias a @RYoda por señalar este punto en los comentarios.
Aquí hay un ejemplo más complicado de Matt Dowle que explica
roll=
:
Encuentre el tiempo para la ocurrencia más cercana de un valor particular para cada fila
Otro ejemplo relacionado: Unirse a la izquierda usando data.table