Назначьте несколько столбцов, используя: = в data.table, по группе

130

Как лучше всего назначить несколько столбцов с помощью data.table? Например:

f <- function(x) {c("hi", "hello")}
x <- data.table(id = 1:10)

Я бы хотел сделать что-то подобное (конечно, это неверный синтаксис):

x[ , (col1, col2) := f(), by = "id"]

И чтобы расширить это, у меня может быть много столбцов с именами, хранящимися в переменной (скажем col_names), и я хотел бы сделать:

x[ , col_names := another_f(), by = "id", with = FALSE]

Как правильно сделать что-то подобное?

Alex
источник
1
Похоже, на него ответили: stackoverflow.com/questions/11308754/…
Alex
Алекс, этот ответ близок, но, похоже, он не работает в сочетании с by@Christoph_J, как правильно сказать. Ссылка на ваш вопрос добавлена ​​в FR # 2120 «Отбросьте необходимость with = FALSE для LHS of: =», чтобы его не забыли вернуться.
Мэтт Доул
Для ясности, f()это функция, возвращающая несколько значений, по одному для каждого из ваших столбцов.
smci

Ответы:

161

Теперь это работает в версии 1.8.3 на R-Forge. Спасибо, что подчеркнули это!

x <- data.table(a = 1:3, b = 1:6) 
f <- function(x) {list("hi", "hello")} 
x[ , c("col1", "col2") := f(), by = a][]
#    a b col1  col2
# 1: 1 1   hi hello
# 2: 2 2   hi hello
# 3: 3 3   hi hello
# 4: 1 4   hi hello
# 5: 2 5   hi hello
# 6: 3 6   hi hello

x[ , c("mean", "sum") := list(mean(b), sum(b)), by = a][]
#    a b col1  col2 mean sum
# 1: 1 1   hi hello  2.5   5
# 2: 2 2   hi hello  3.5   7
# 3: 3 3   hi hello  4.5   9
# 4: 1 4   hi hello  2.5   5
# 5: 2 5   hi hello  3.5   7
# 6: 3 6   hi hello  4.5   9 

mynames = c("Name1", "Longer%")
x[ , (mynames) := list(mean(b) * 4, sum(b) * 3), by = a]
#     a b col1  col2 mean sum Name1 Longer%
# 1: 1 1   hi hello  2.5   5    10      15
# 2: 2 2   hi hello  3.5   7    14      21
# 3: 3 3   hi hello  4.5   9    18      27
# 4: 1 4   hi hello  2.5   5    10      15
# 5: 2 5   hi hello  3.5   7    14      21
# 6: 3 6   hi hello  4.5   9    18      27


x[ , get("mynames") := list(mean(b) * 4, sum(b) * 3), by = a][]  # same
#    a b col1  col2 mean sum Name1 Longer%
# 1: 1 1   hi hello  2.5   5    10      15
# 2: 2 2   hi hello  3.5   7    14      21
# 3: 3 3   hi hello  4.5   9    18      27
# 4: 1 4   hi hello  2.5   5    10      15
# 5: 2 5   hi hello  3.5   7    14      21
# 6: 3 6   hi hello  4.5   9    18      27

x[ , eval(mynames) := list(mean(b) * 4, sum(b) * 3), by = a][]   # same
#    a b col1  col2 mean sum Name1 Longer%
# 1: 1 1   hi hello  2.5   5    10      15
# 2: 2 2   hi hello  3.5   7    14      21
# 3: 3 3   hi hello  4.5   9    18      27
# 4: 1 4   hi hello  2.5   5    10      15
# 5: 2 5   hi hello  3.5   7    14      21
# 6: 3 6   hi hello  4.5   9    18      27

Более старая версия, использующая withаргумент (мы не рекомендуем этот аргумент, когда это возможно):

x[ , mynames := list(mean(b) * 4, sum(b) * 3), by = a, with = FALSE][] # same
#    a b col1  col2 mean sum Name1 Longer%
# 1: 1 1   hi hello  2.5   5    10      15
# 2: 2 2   hi hello  3.5   7    14      21
# 3: 3 3   hi hello  4.5   9    18      27
# 4: 1 4   hi hello  2.5   5    10      15
# 5: 2 5   hi hello  3.5   7    14      21
# 6: 3 6   hi hello  4.5   9    18      27
Мэтт Доул
источник
Спасибо за этот ответ и примеры. Как мне изменить следующую строку, чтобы получить два столбца для каждого objectName из нечеткого вывода, а не один столбец с двумя строками? data.table(objectName=ls())[,c("rows","cols"):=dim(get(objectName)),by=objectName]( data.table
Пользуюсь
@dnlbrky dimвозвращает вектор, поэтому преобразование его в тип listдолжно повернуть его; напр [,c("rows","cols"):=as.list(dim(get(objectName))),by=objectNa‌​me]. Проблема в том, что as.listнакладные расходы на вызовы также копируют небольшой вектор. Если при увеличении количества групп возникает проблема с эффективностью, сообщите нам об этом.
Мэтт Доул
1
Привет, Мэтт. Первый пример во втором блоке кода (т.е. x[,mynames:=list(mean(b)*4,sum(b)*3),by=a,with=FALSE][]) теперь выдает предупреждение, может быть, удалить его? В связи с этим, кто-нибудь предлагал, чтобы options(datatable.WhenJisSymbolThenCallingScope=TRUE)такое задание x[,mynames:=list(mean(b)*4,sum(b)*3),by=a]действительно работало? Похоже, это согласуется с другими изменениями, хотя я предполагаю, что это может сломать слишком много существующего пользовательского кода (?).
Джош О'Брайен
1
@PanFrancisco Без by=aнего будет работать, но верните другой ответ. mean(a)И sum(a)агрегаты перерабатывается в пределах каждой группы , когда by=a. Без by=aнего просто вставляет meanи sumдля всего столбца в каждую ячейку (т.е. разные числа).
Мэтт Доул
1
@MattDowle, что, если моя функция уже возвращает именованный список, могу ли я добавить столбцы в dt, не называя их снова? например, f <- function (x) {list ("c" = "hi", "d" = "hello")} будет печатать результаты с именованными столбцами с x [, f (), by = a] []. Я не знаю, как добавить результат к dt.
Jfly 05