Выбрать / присвоить 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

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

6 ответов

Решение

Два способа программного выбора переменных:

  1. with = FALSE:

    DT = data.table(col1 = 1:3)
    colname = "col1"
    DT[, colname, with = FALSE] 
    #    col1
    # 1:    1
    # 2:    2
    # 3:    3
    
  2. 'точка точка' (..) префикс:

    DT[, ..colname]    
    #    col1
    # 1:    1
    # 2:    2
    # 3:    3
    

Для дальнейшего описания "точка точка" (..), см. Новые функции в 1.10.2 (в настоящее время это не описано в тексте справки).

Чтобы присвоить переменную (и), оберните LHS из := в скобках:

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

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

С помощью with = FALSE с := в настоящее время считается устаревшим во всех случаях, учитывая, что := с круглыми скобками был предпочтительным в течение некоторого времени.

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 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

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

В любом случае, для тех, кто действительно хочет создать новый столбец в таблице данных с именем, хранящимся в переменной, у меня есть для работы следующее. Я понятия не имею, как это работает. Есть предложения по улучшению? Можно ли предположить, что новый столбец без имени всегда будет иметь имя 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(), но не могу заставить его назначить на том же шаге. Кстати, причина, по которой мне нужно это сделать, заключается в том, что colname будет зависеть от пользовательского ввода в приложении Shiny.

Получить несколько столбцов из таблицы данных с помощью переменной или функции:

library(data.table)

x <- data.table(this=1:2,that=1:2,whatever=1:2)

# === explicit call
x[, .(that, whatever)]
x[, c('that', 'whatever')]

# === indirect via  variable
# ... direct assignment
mycols <- c('that','whatever')
# ... same as result of a function call
mycols <- grep('a', colnames(x), value=TRUE)

x[, ..mycols]
x[, .SD, .SDcols=mycols]

# === direct 1-liner usage
x[, .SD, .SDcols=c('that','whatever')]
x[, .SD, .SDcols=grep('a', colnames(x), value=TRUE)]

которые все дают

   that whatever
1:    1        1
2:    2        2

Я нахожу .SDcols кстати самый изящный.

С версией разработки 1.14.3 data.table получил новый интерфейс для программирования на data.table , см. пункт 10 в Новые возможности . Он использует новый env =параметр.

      library(data.table) # development version 1.14.3 used
dt <- data.table(col1 = 1:3)
colname <- "col1"

dt[, cn := cn + 3L, env = list(cn = colname)][]
           col1
   <int>
1:     4
2:     5
3:     6

Вы можете попробовать это

colname <- as.name ("COL_NAME")

DT2 <- DT [, list (COL_SUM = sum (eval (colname,.SD))), by = c(group)]

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

При обновлении значений из функции 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" 
Другие вопросы по тегам