Выберите/назначьте переменные data.table, имена которых хранятся в символьном векторе

Как вы относитесь к переменным в data.table, если имена переменных хранятся в символьном векторе? Например, это работает для data.frame:

df <- data.frame(col1 = 1:3)
colname <- "col1"
df[colname] <- 4:6
df
#   col1
# 1    4
# 2    5
# 3    6

Как я могу выполнить эту же операцию для таблицы данных с нотной записью := или без нее? Очевидная вещь dt[ , list(colname)] не работает (и я этого не ожидал).

Ответ 1

Попробуйте:

DT = data.table(col1 = 1:3)
colname = "col1"

DT[, colname, with = FALSE]    # select
#    col1
# 1:    1
# 2:    2
# 3:    3

DT[, (colname) := 4:6]    # assign
#    col1
# 1:    4
# 2:    5
# 3:    6

Последний известен как столбец plonk, потому что вы заменяете весь вектор столбца ссылкой. Если присутствует подмножество i, оно будет переназначать по ссылке. Параны вокруг (colname) - это стенография, введенная в версии v1.9.4 на CRAN Oct 2014. Вот новость:

Using `with = FALSE` with `:=` is now deprecated in all cases, given that wrapping
the LHS of `:=` with parentheses has been preferred for some time.

colVar = "col1"
DT[, colVar := 1, with = FALSE]                   # deprecated, still works silently
DT[, (colVar) := 1]                             # please change to this
DT[, c("col1", "col2") := 1]                    # no change
DT[, 2:4 := 1]                                  # no change
DT[, c("col1","col2") := list(sum(a), mean(b)]  # no change
DT[, `:=`(...), by = ...]                       # no change

См. также раздел Подробности в ?`:=`:

DT[i, (colnamevector) := value]
# [...] The parens are enough to stop the LHS being a symbol

И чтобы ответить на следующий вопрос в комментарии, здесь один из способов (как обычно, существует много способов):

DT[, colname := cumsum(get(colname)), with = FALSE]
#    col1
# 1:    4
# 2:    9
# 3:   15 

или вам может быть проще читать, писать и отлаживать только до eval a paste, подобно построению динамического оператора SQL для отправки на сервер:

expr = paste0("DT[,",colname,":=cumsum(",colname,")]")
expr
# [1] "DT[,col1:=cumsum(col1)]"

eval(parse(text=expr))
#    col1
# 1:    4
# 2:   13
# 3:   28

Если вы так много сделаете, вы можете определить вспомогательную функцию eval:

EVAL = function(...)eval(parse(text=paste0(...)),envir=parent.frame(2))

EVAL("DT[,",colname,":=cumsum(",colname,")]")
#    col1
# 1:    4
# 2:   17
# 3:   45

Теперь, когда data.table 1.8.2 автоматически оптимизирует j для эффективности, может быть предпочтительнее использовать метод eval. get() в j предотвращает некоторые оптимизации, например.

Или существует set(). Низкая служебная, функциональная форма :=, что было бы неплохо. См. ?set.

set(DT, j = colname, value = cumsum(DT[[colname]]))
DT
#    col1
# 1:    4
# 2:   21
# 3:   66

Ответ 2

* Это не ответ на самом деле, но у меня нет достаточного количества уличных кредитов для комментариев:/

В любом случае, для тех, кто может попытаться создать новый столбец в таблице данных с именем, хранящимся в переменной, у меня есть следующее: Я не имею в виду, что это производительность. Любые предложения по улучшению? Можно ли предположить, что безымянный новый столбец всегда будет иметь имя V1?

colname <- as.name("users")
# Google Analytics query is run with chosen metric and resulting data is assigned to DT
DT2 <- DT[, sum(eval(colname, .SD)), by = country]
setnames(DT2, "V1", as.character(colname))

Заметьте, что я могу ссылаться на нее просто отлично в sum(), но, похоже, не может ее назначить на том же шаге. BTW, причина, по которой мне нужно это сделать, это colname будет основываться на вводе пользователя в приложении Shiny.

Ответ 3

Для нескольких столбцов и функции, применяемой к значениям столбца.

При обновлении значений из функции RHS должен быть объектом списка, поэтому использование цикла на .SD с помощью lapply сделает трюк.

Нижеприведенный пример преобразует целочисленные столбцы в числовые столбцы

a1 <- data.table(a=1:5, b=6:10, c1=letters[1:5])
sapply(a1, class)  # show classes of columns
#         a           b          c1 
# "integer"   "integer" "character" 

# column name character vector
nm <- c("a", "b")

# Convert columns a and b to numeric type
a1[, j = (nm) := lapply(.SD, as.numeric ), .SDcols = nm ]

sapply(a1, class)
#         a           b          c1 
# "numeric"   "numeric" "character"