Почему X [Y] присоединяется к data.tables, не допускает полного внешнего соединения или левого соединения?
Это немного философский вопрос о синтаксисе соединения data.table. Я нахожу все больше и больше использования для data.tables, но все еще учась...
Формат соединения X[Y]
для data.tables очень краткий, удобный и эффективный, но, насколько я могу судить, он поддерживает только внутренние соединения и внешние внешние соединения. Чтобы получить левое или полное внешнее соединение, мне нужно использовать merge
:
-
X[Y, nomatch = NA]
- все строки в Y - правое внешнее соединение (по умолчанию)
-
X[Y, nomatch = 0]
- только строки с совпадениями в X и Y - внутреннее соединение
-
merge(X, Y, all = TRUE)
- все строки из X и Y - полное внешнее соединение
-
merge(X, Y, all.x = TRUE)
- все строки в X-левом внешнем соединении
Мне кажется, что было бы удобно, если формат объединения X[Y]
поддерживал все 4 типа объединений. Есть ли причина, по которой поддерживаются только два типа соединений?
Для меня значения параметров nomatch = 0
и nomatch = NA
не очень интуитивно понятны для выполняемых действий. Мне легче понять и запомнить синтаксис merge
: all = TRUE
, all.x = TRUE
и all.y = TRUE
. Поскольку операция X[Y]
напоминает merge
намного больше, чем match
, почему бы не использовать синтаксис merge
для объединений, а не параметр match
nomatch
параметр <
Вот примеры кода из 4 типов соединений:
# sample X and Y data.tables
library(data.table)
X <- data.table(t = 1:4, a = (1:4)^2)
setkey(X, t)
X
# t a
# 1: 1 1
# 2: 2 4
# 3: 3 9
# 4: 4 16
Y <- data.table(t = 3:6, b = (3:6)^2)
setkey(Y, t)
Y
# t b
# 1: 3 9
# 2: 4 16
# 3: 5 25
# 4: 6 36
# all rows from Y - right outer join
X[Y] # default
# t a b
# 1: 3 9 9
# 2: 4 16 16
# 3: 5 NA 25
# 4: 6 NA 36
X[Y, nomatch = NA] # same as above
# t a b
# 1: 3 9 9
# 2: 4 16 16
# 3: 5 NA 25
# 4: 6 NA 36
merge(X, Y, by = "t", all.y = TRUE) # same as above
# t a b
# 1: 3 9 9
# 2: 4 16 16
# 3: 5 NA 25
# 4: 6 NA 36
identical(X[Y], merge(X, Y, by = "t", all.y = TRUE))
# [1] TRUE
# only rows in both X and Y - inner join
X[Y, nomatch = 0]
# t a b
# 1: 3 9 9
# 2: 4 16 16
merge(X, Y, by = "t") # same as above
# t a b
# 1: 3 9 9
# 2: 4 16 16
merge(X, Y, by = "t", all = FALSE) # same as above
# t a b
# 1: 3 9 9
# 2: 4 16 16
identical( X[Y, nomatch = 0], merge(X, Y, by = "t", all = FALSE) )
# [1] TRUE
# all rows from X - left outer join
merge(X, Y, by = "t", all.x = TRUE)
# t a b
# 1: 1 1 NA
# 2: 2 4 NA
# 3: 3 9 9
# 4: 4 16 16
# all rows from both X and Y - full outer join
merge(X, Y, by = "t", all = TRUE)
# t a b
# 1: 1 1 NA
# 2: 2 4 NA
# 3: 3 9 9
# 4: 4 16 16
# 5: 5 NA 25
# 6: 6 NA 36
Обновление: data.table v1.9.6 представил синтаксис on=
, который позволяет ad hoc присоединяться к полям, отличным от первичного ключа. jangorecki answer на вопрос Как объединить (объединить) кадры данных (внутренний, внешний, левый, правый)? содержит некоторые примеры дополнительных типов соединений, которые может обрабатывать data.table.
Ответы
Ответ 1
Для цитаты из data.table FAQ 1.12
1.12
В чем разница между X [Y] и слиянием (X, Y)?
-
X[Y]
- это объединение, поиск X строк с использованием Y (или Y-ключ, если он есть) в качестве индекса. -
Y[X]
- это объединение, поиск Y строк с использованием X (или X-ключ, если он есть) -
merge(X,Y)
выполняет оба способа одновременно.
Количество строк X[Y]
и Y[X]
обычно отличается; тогда как число строки, возвращаемые merge(X,Y)
и merge(Y,X)
, совпадают. Но затем не хватает основной точки. Большинство задач требуют, чтобы что-то было сделано на данные после объединения или объединения. Зачем объединяются все столбцы данных, только для используйте небольшое подмножество из них потом? Вы можете предложить merge(X[,ColsNeeded1],Y[,ColsNeeded2])
, но это копирует подмножеств данных, и для этого требуется программист, столбцы. X[Y,j
] в data.table делает все это за один шаг для вы. Когда вы пишете X[Y,sum(foo*bar)]
, data.table автоматически проверяет выражение j, чтобы увидеть, какие столбцы он использует. Это будет подмножество только этих столбцов; остальные игнорируются. Память только созданный для столбцов, используемых j, и Y столбцов имеют стандартный R правила рециркуляции в контексте каждой группы. Пусть говорят, что foo находится в X и bar находится в Y (вместе с 20 другими столбцами в Y). не X[Y,sum(foo*bar)]
быстрее программировать и быстрее запускать, чем слияние а затем подмножество?
Если вы хотите левое внешнее соединение X[Y]
le <- Y[X]
mallx <- merge(X, Y, all.x = T)
# the column order is different so change to be the same as `merge`
setcolorder(le, names(mallx))
identical(le, mallx)
# [1] TRUE
Если вы хотите полное внешнее соединение
# the unique values for the keys over both data sets
unique_keys <- unique(c(X[,t], Y[,t]))
Y[X[J(unique_keys)]]
## t b a
## 1: 1 NA 1
## 2: 2 NA 4
## 3: 3 9 9
## 4: 4 16 16
## 5: 5 25 NA
## 6: 6 36 NA
# The following will give the same with the column order X,Y
X[Y[J(unique_keys)]]
Ответ 2
Ответ на @mnel - это точка, так что примите этот ответ. Это просто продолжение, слишком долго для комментариев.
Как говорит mnel, левое/правое внешнее соединение получается путем замены Y
и X
: Y[X]
-vs- X[Y]
. Таким образом, в этом синтаксисе поддерживаются 3 из 4 типов соединений, а не 2, iiuc.
Добавление четвертого кажется хорошей идеей. Скажем, мы добавляем full=TRUE
или both=TRUE
или merge=TRUE
(не уверен, что имя лучшего аргумента?), Тогда мне не приходило в голову, прежде чем X[Y,j,merge=TRUE]
будет полезно по причинам после BUT в FAQ 1.12. Новый запрос функции теперь добавлен и связан здесь, спасибо:
FR # 2301: Добавить параметр merge = TRUE для X [Y] и Y [X] join, как merge().
Недавние версии ускорили merge.data.table
(например, сделав мелкую копию внутри, чтобы более эффективно устанавливать ключи). Поэтому мы пытаемся приблизить merge()
и X[Y]
и предоставить все возможности пользователю для полной гибкости. Есть плюсы и минусы обоих. Еще один выдающийся запрос функции:
FR # 2033: Добавить by.x и by.y в файл merge.data.table
Если есть другие, пожалуйста, продолжайте их приходить.
В этой части вопроса:
почему бы не использовать синтаксис слияния для объединений, а не параметр номенклатуры функции соответствия?
Если вы предпочитаете синтаксис merge()
и его 3 аргумента all
, all.x
и all.y
, просто используйте это вместо X[Y]
. Думайте, что он должен охватывать все случаи. Или вы имели в виду, почему аргументом является один nomatch
в [.data.table
? Если это так, то это просто так, как это казалось естественным, учитывая FAQ 2.14: "Не могли бы вы объяснить, почему data.table вдохновляется синтаксисом A [B] в базе?". Но также, nomatch
принимает только два значения в настоящее время 0
и NA
. Это может быть расширено, чтобы отрицательное значение означало что-то, или 12 означало бы использовать значения 12-й строки для заполнения NA, например, или nomatch
в будущем может быть вектором или даже самим data.table
.
Hm. Как бы без взаимодействия взаимодействовать с merge = TRUE? Возможно, мы должны принять это к datatable-help.
Ответ 3
Этот "ответ" - это предложение для обсуждения: как указано в моем комментарии, я предлагаю добавить параметр join
в [.data.table(), чтобы включить дополнительные типы объединений, то есть: X[Y,j,join=string]
. В дополнение к 4 типам обычных объединений я также предлагаю поддерживать 3 типа эксклюзивных объединений и перекрестное соединение.
Строковые значения join
(и псевдонимы) для различных типов соединений предлагаются как:
-
"all.y"
и "right"
- правое соединение, настоящие данные. таблица default (nomatch = NA) - все Y строк с NA, где нет соответствия X;
-
"both"
и "inner"
- внутреннее соединение (nomatch = 0) - только строки, где соответствуют X и Y;
-
"all.x"
и "left"
- left join - все строки из X, NA, где нет Y:
-
"outer"
и "full"
- полное внешнее объединение - все строки из X и Y, NA, где нет соответствия
-
"only.x"
и "not.y"
- non-join или anti-join, возвращающие строки X, где нет соответствия Y
-
"only.y"
и "not.x"
- non-join или anti-join, возвращающие Y строк, где нет соответствия X
-
"not.both"
- эксклюзивное соединение, возвращающее строки X и Y, где нет соответствия другой таблице, то есть исключающее или (XOR)
-
"cross"
- поперечное объединение или декартово произведение с каждой строкой X, сопоставляемой каждой строке Y
Значение по умолчанию join="all.y"
, которое соответствует текущему по умолчанию.
Строковые значения "all", "all.x" и "all.y" соответствуют параметрам merge()
. "Правильные", "левые", "внутренние" и "внешние" строки могут быть более пригодными для пользователей SQL.
"Оба" и "not.both" строки - мое лучшее предложение на данный момент - но у кого-то могут быть более строгие предложения для внутреннего соединения и эксклюзивного соединения. (Я не уверен, что "эксклюзивный" - это правильная терминология, исправьте меня, если есть правильный термин для присоединения "XOR".)
Использование join="not.y"
является альтернативой для синтаксиса X[-Y,j]
или X[!Y,j]
несовместимости и, возможно, более ясным (для меня), хотя я не уверен, что они одинаковы (новая функция в data.table версия 1.8.3).
Перекрестное соединение иногда может быть полезным, но оно может не соответствовать парадигме data.table.