Data.table join и j-выражение неожиданное поведение
В R 2.15.0
и data.table 1.8.9
:
d = data.table(a = 1:5, value = 2:6, key = "a")
d[J(3), value]
# a value
# 3 4
d[J(3)][, value]
# 4
Я ожидал, что оба будут производить тот же результат (второй), и я считаю, что они должны.
В интересах выяснения того, что это не проблема синтаксиса J
, такое же ожидание относится к следующим (идентичным выше) выражениям:
t = data.table(a = 3, key = "a")
d[t, value]
d[t][, value]
Я бы ожидал, что оба из них вернут тот же результат.
Итак, позвольте мне перефразировать вопрос - почему (data.table
сконструирован так, чтобы) ключевой столбец автоматически распечатывался в d[t, value]
?
Обновление (на основе ответов и комментариев ниже): Спасибо @Arun и др., я понимаю дизайн - почему сейчас. Причина, по которой выше выдает ключ, заключается в том, что каждый раз, когда вы выполняете слияние data.table
с помощью синтаксиса X[Y]
, существует скрытая информация, а by
- ключ. Причина, по которой он сконструирован таким образом, выглядит следующим образом: поскольку операция by
должна выполняться при слиянии, можно также воспользоваться этим и не делать другого by
, если вы собираетесь сделать это с помощью ключа слияния.
Теперь, когда я сказал, я считаю, что недостаток дизайна синтаксиса. То, как я читаю синтаксис data.table
d[i, j, by = b]
, это
возьмите d
, примените операцию i
(будь то подмножество или слияние или что-то еще), а затем выполните выражение J
"на" b
Пошаговое разрывы этого чтения и вводятся случаи, о которых нужно думать конкретно (я слияния на i
, by
просто ключ слияния и т.д.). Я считаю, что это должно быть задачей data.table
- похвальное усилие сделать data.table
быстрее в одном конкретном случае слияния, когда by
равен ключу, должно выполняться альтернативным способом (например, путем проверки внутри, если выражение by
фактически является ключом слияния).
Ответы
Ответ 1
По data.table 1.9.3
, поведение по умолчанию было изменено, а приведенные ниже примеры дают тот же результат. Чтобы получить результат by-without-by, теперь нужно указать явный by=.EACHI
:
d = data.table(a = 1:5, value = 2:6, key = "a")
d[J(3), value]
#[1] 4
d[J(3), value, by = .EACHI]
# a value
#1: 3 4
И вот несколько более сложный пример, иллюстрирующий разницу:
d = data.table(a = 1:2, b = 1:6, key = 'a')
# a b
#1: 1 1
#2: 1 3
#3: 1 5
#4: 2 2
#5: 2 4
#6: 2 6
# normal join
d[J(c(1,2)), sum(b)]
#[1] 21
# join with a by-without-by, or by-each-i
d[J(c(1,2)), sum(b), by = .EACHI]
# a V1
#1: 1 9
#2: 2 12
# and a more complicated example:
d[J(c(1,2,1)), sum(b), by = .EACHI]
# a V1
#1: 1 9
#2: 2 12
#3: 1 9
Ответ 2
Изменить число Бесконечность: Faq 1.12 точно отвечает на ваш вопрос: (Также полезно/релевантно FAQ 1.13, не вставлен здесь).
1.12 В чем разница между X [Y] и слиянием (X, Y)?
X [Y] является соединением, ища строки X, используя Y (или Y-ключ, если он есть) в качестве индекса. Y [X] является объединением, ища Y строк, используя X (или X-ключ, если он есть) в качестве индекса. merge (X, Y) 1 делает оба пути одновременно. Число строк X [Y] и Y [X] обычно меньше; тогда как число строк, возвращаемых слиянием (X, Y) и слиянием (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)]
быстрее работать и быстрее запускать, чем слияние, за которым следует подмножество?
Старый ответ, который ничего не ответил на вопрос OP (из комментария OP), сохранился здесь, потому что я считаю, что это так).
Когда вы даете значение j
, например d[, 4]
или d[, value]
в data.table
, j
оценивается как expression
. Из таблицы data.table FAQ 1.1 о доступе к DT[, 5]
(самые первые FAQ):
Поскольку по умолчанию, в отличие от data.frame, второй аргумент является выражением, которое оценивается в пределах области DT. 5 - 5.
Первое, поэтому, чтобы понять, в вашем случае:
d[, value] # produces a "vector"
# [1] 2 3 4 5 6
Это не так, когда запрос для i
является базовым индексированием вроде:
d[3, value] # produces a vector of length 1
# [1] 4
Однако это другое, когда i
само по себе a data.table
. Из введения data.table
(стр. 6):
d[J(3)] # is equivalent to d[data.table(a = 3)]
Здесь вы выполняете join
. Если вы просто сделаете d[J(3)]
, тогда вы получите все столбцы, соответствующие этому соединению. Если вы это сделаете,
d[J(3), value] # which is equivalent to d[J(3), list(value)]
Поскольку вы говорите, что этот ответ ничего не отвечает на ваш вопрос, я укажу, где ответ на ваш "перефразированный" вопрос, я считаю, лежит: --- > , тогда вы получите только эту колонку, но так как вы выполняете соединение, также будет выведено значение key'd (поскольку это соединение между двумя таблицами на основе столбца ключа).
Изменить: Следуя вашему 2-му правлению, если ваш вопрос - почему так?, то я неохотно (или, скорее, неосведомленно) ответил бы, Мэтью Доулл сконструировал так, чтобы различать data.table join-based-subset
и a index-based-subset
.
Второй синтаксис эквивалентен:
d[J(3)][, value] # is equivalent to:
dd <- d[J(3)]
dd[, value]
где, опять же, в dd[, value]
, j
оценивается как выражение, и поэтому вы получаете вектор.
Чтобы ответить на ваш 3-й измененный вопрос: в третий раз это потому, что он представляет собой СОЕДИНЕНИЕ между двумя data.tables на основе столбца ключа. Если я присоединяюсь к двум data.table
s, я ожидал бы data.table
Из введения data.table
еще раз:
Передача data.table в подмножество data.table аналогична синтаксису A [B] в базе R, где A - матрица, а B - матрица с двумя столбцами. Фактически синтаксис A [B] в базе R вдохновил пакет data.table.
Ответ 3
Это не неожиданное поведение, это документированное поведение. Арун неплохо объяснил и продемонстрировал в FAQ, где это отчетливо документально.
есть запрос функции FR 1757, в котором предлагается использовать аргумент drop
в этом случае
При реализации, поведение, которое вы хотите, может быть закодировано
d = data.table(a = 1:5, value = 2:6, key = "a")
d[J(3), value, drop = TRUE]
Ответ 4
Я согласен с ответом Аруна. Вот еще одна формулировка: после того, как вы присоединитесь, вы часто будете использовать столбец объединения в качестве ссылки или как вход для дальнейшего преобразования. Таким образом, вы сохраняете его, и у вас есть возможность отбросить его с помощью двойного синтаксиса [
(более кругового). С точки зрения дизайна легче хранить часто релевантную информацию и затем отбрасывать, когда это необходимо, чем отбрасывать раньше и рисковать потерять данные, которые трудно восстановить.
Еще одна причина, по которой вы хотите сохранить столбец join, заключается в том, что вы можете выполнять агрегированные операции одновременно с выполнением соединения (без него). Например, результаты здесь намного яснее, включая столбец соединения:
d <- data.table(a=rep.int(1:3,2),value=2:7,other=100:105,key="a")
d[J(1:3),mean(value)]
# a V1
#1: 1 3.5
#2: 2 4.5
#3: 3 5.5