Почему 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.