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