Data.table: почему не всегда можно передавать имена столбцов напрямую?

Начало работы с пакетом data.table (автор/сопровождающий: Matt Dowle). Отличный пакет. Мне нравится, что я могу написать dt[, x1] вместо, скажем, dt[, dt$x1] или df["x1"], для data.table dt, имя столбца x1, data.frame df. Возможность передавать имена столбцов напрямую является привлекательной особенностью data.table. Но выдача кавычек вокруг имени столбца (запись x1 вместо "x1") не всегда возможна. Почему?

Вопрос о программировании: Существуют ли причины, по которым не всегда возможно передать вектор имен столбцов непосредственно в таблицу данных.и или вспомогательные функции, предоставляемые пакетом? Например, функции subset, merge и melt были переписаны для пакета data.table, но в то время как subset может обрабатывать имена столбцов напрямую, merge и melt не может (см. Ниже),

Чтобы уточнить, мой вопрос не в том, когда и почему, а почему. Есть прекрасные связанные обсуждения с очень полезными советами, например. Выберите/назначьте переменные data.table, имена которых хранятся в символьном векторе и r - передают переменные в качестве столбца data.table имена. С этими ответами и небольшим количеством проб и ошибок я могу найти свой путь вокруг различий цитата /unquote. Мой вопрос заключается в том, почему в настоящее время невозможно всегда обойтись без котировок вокруг имен столбцов: есть ли для этого дизайн? это переходная ситуация? существуют ли трудности программирования?

Ниже приводятся некоторые примеры и приведены примеры для ясности.

# load the package
library("data.table") # because I cannot do install.packages(data.table)!!

(я)

# make a data.table
set.seed(1)
dt <- data.table(id = 1:5, x1 = 1:5, x2 = 5:1, x3 = round(runif(5, 1, 5), 0), key = "id")

Я могу определить таблицу данных с помощью id = 1:10 или "id" = 1:10, но я должен определить ключ с key = "id", поскольку key = id не работает:

dt <- data.table(id = 1:5, x1 = 1:5, x2 = 5:1, x3 = round(runif(5, 1, 5), 0), key = id)
##Error in data.table(id = 1:5, x1 = 1:5, x2 = 5:1, x3 = round(runif(5,  : 
##  object 'id' not found

Вы думаете, что найти "id" должно быть довольно легко для ключа, если бы он искал его среди имен столбцов? Будет ли программно звук разрешенным, чтобы бросить кавычки в RHS key?

(II)

Я могу subset с вектором столбцов или с вектором имен столбцов:

subset(dt, select = c(x1, x3))
##   x1 x3
##1:  1  2
##2:  2  2
##3:  3  3
##4:  4  5
##5:  5  2

subset(dt, select = c("x1", "x3"))
##   x1 x3
##1:  1  2
##2:  2  2
##3:  3  3
##4:  4  5
##5:  5  2

Хороший и гибкий.

(III)

Я могу merge с вектором имен столбцов:

merge(dt, dt, by = c("x1", "x2"))
##       id x1 x2 x3
##1:  1  1  5  2
##2:  2  2  4  2
##3:  3  3  3  3
##4:  4  4  2  5
##5:  5  5  1  2

(глупый пример, который был!), но не с вектором столбцов:

merge(dt, dt, by = c(x1, x2))
##Error in merge.data.table(dt, dt, by = c(x1, x2)) : object 'x1' not found

Есть ли что-то о merge, которое препятствует принятию вектора столбцов способом subset?

(IV)

Аналогично, melt должен принимать цитируемые имена столбцов (или целые числа, соответствующие номерам столбцов).

Конкретное описание справки: melt принимает "векторы символов", а help для merge просто указывает "векторы имен столбцов", но, очевидно, с merge, как и с melt, векторы символов ожидаются.

(v)

В случае аргумента j цитирование имен переменных обычно не является правильным:

# Good:
dt[, .(x1, x2)]
##   x1 x2
##1:  1  5
##2:  2  4
##3:  3  3
##4:  4  2
##5:  5  1

# Bad 
dt[, .("x1", "x2")]
##   V1 V2
##1: x1 x2
# This feature is well documented in the FAQs
# FAQ 2.3: "I'm using c() in the j and getting strange results."

Обратите внимание на то, что читатель совсем не знаком с data.tables, что .() является сокращением для list() и что dt[, c(x1, x2)] вряд ли будет желаемой командой здесь - аргумент j dt[i, j] очень ожидает список.

(VI)

Однако, в аргументе j dt[i, j], LHS оператора "присвоение по ссылке" := имеет смутное соглашение.

Если LHS - это один столбец, он может быть передан без кавычек. Но если он имеет несколько столбцов, они должны быть переданы как вектор названий столбцов. В руководстве указано только "вектор имен столбцов", но эксперименты предполагают, что они должны быть указаны:

# Good:
dt[, c("x1", "x2") := NULL][]
##   id x3
##1:  1  2
##2:  2  2
##3:  3  3
##4:  4  5
##5:  5  2

# Bad:
dt[, c(x1, x2) := NULL]
##Error in eval(expr, envir, enclos) : object 'x1' not found

Сообщение об ошибке не особенно полезно. Но теперь я помню совет по часто задаваемым вопросам: "Если требуется 2 или более столбцов, используйте вместо него список() или.()". Глупый я, c(x1, x2) не мог работать, потому что нет способа сказать, где заканчивается x1 и x2. Однако .(x1, x2) может работать, не так ли?

# Bad:
dt[, .(x1, x2) := NULL]
##Error in eval(expr, envir, enclos) : object 'x1' not found

Нет, все рассмотренные вещи, LHS := ожидает вектор названий столбцов. Руководство должно быть обновлено или, если это возможно, data.table расширено, чтобы принимать списки некотируемых столбцов на LHS.

О, подождите. Чтобы удалить несколько имен столбцов, могу ли я передать список цитируемых имен в LHS? Нет. Списки обычно желательны, но не на LHS :=. Сообщение об ошибке ясно:

# Bad:
dt[, .("x1", "x2") := NULL][]
##Error in `[.data.table`(dt, , `:=`(.("x1", "x2"), NULL)) : 
##  LHS of := must be a symbol, or an atomic vector (column names or positions).

(VII)

Аргумент i dt[i] также предназначен для приема некотируемых столбцов, то есть "выражение имен столбцов"

dt[.(x1, x2)]
##   id x1 x2 x3 V2
##1:  1  1  5  2  5
##2:  2  2  4  2  4
##3:  3  3  3  3  3
##4:  4  4  2  5  2
##5:  5  5  1  2  1

Обратите внимание, что если идея заключалась в подмножестве двух столбцов x1 и x2, это должно быть сделано внутри аргумента j, т.е. dt[,.(x1, x2)]

dt[.("x1", "x2")]
##Error in bmerge(i, x, leftcols, rightcols, io, xo, roll, rollends, nomatch,  : 
##  typeof x.id (integer) != typeof i.V1 (character)

dt[c(x1, x2)]
##id x1 x2 x3
## 1:  1  1  5  2
## 2:  2  2  4  2
## 3:  3  3  3  3
## 4:  4  4  2  5
## 5:  5  5  1  2
## 6:  5  5  1  2
## 7:  4  4  2  5
## 8:  3  3  3  3
## 9:  2  2  4  2
##10:  1  1  5  2

dt[c("x1", "x2")]
##Error in bmerge(i, x, leftcols, rightcols, io, xo, roll, rollends, nomatch,  : 
##  typeof x.id (integer) != typeof i.V1 (character)

Здесь я привел несколько ситуаций, когда столбцы должны быть переданы как x1 или как "x1", и ситуации, в которых они могут выполняться. Эти различия могут вызвать путаницу для новых пользователей, таких как я. Я подозреваю, что существует несколько причин для этих двух подходов к сосуществованию. Я был бы признателен, если бы кто-нибудь мог прояснить этот вопрос, для некоторых из моих примеров, если не для всех.

Ответы

Ответ 1

(i), (iii) и (iv) звучат как запросы функций (FR); см. здесь (так, да, это отчасти из-за data.table не достигнув полной зрелости).

Что касается (v), вы сказали, что "dt[, c(x1, x2)] вряд ли будет желаемой командой здесь", но на самом деле я видел ситуации, когда такое использование c внутри j - это то, что я получаю после, Ситуации, подобные (v), относятся к аргументу with [.data.table.

В (vi) и в другом месте вы предлагаете "В руководстве указано только" вектор имен столбцов ", но эксперименты предполагают, что они должны быть указаны"; но я думаю, что это недвусмысленно. Вектор имен столбцов означает вектор character, который c(x1,x2) не является, если x1 и x2 не определены как character векторы сами. Вы также можете добавить FR для документации по GitHub.

Я не уверен, что вам нужно в (vii), но в i векторы имен используются для объединений или подстановленных подмножеств (также форма соединения); см. виньетку на быстрой подмножестве.