Выбрать / присвоить 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 ответов
Два способа программного выбора переменных:
with = FALSE
:DT = data.table(col1 = 1:3) colname = "col1" DT[, colname, with = FALSE] # col1 # 1: 1 # 2: 2 # 3: 3
'точка точка' (
..
) префикс: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"