按组在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]

做这样的事情的正确方法是什么?


1
看起来好像已经得到了答复: stackoverflow.com/questions/11308754/…–
Alex

亚历克斯,这个答案很接近,但似乎无法与by@Christoph_J正确地结合使用。链接到已添加到FR#2120的问题 “对于LHS为:=“,需要丢弃== FALSE,这样就不会忘记再次访问它。
马特·道尔

需要明确的f()是,该函数返回多个值,每个列一个。
smci

Answers:


161

现在可以在R-Forge的v1.8.3中使用。感谢您突出显示它!

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

感谢您提供的答案和示例。我应该如何修改下一行,以便从dim输出中为每个objectName获得两列,而不是由两行组成一列?data.table(objectName=ls())[,c("rows","cols"):=dim(get(objectName)),by=objectName](我使用的是data.table1.8.11)
dnlbrky 2014年

@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它,则将整个列的meansum粘贴到每个单元格中(即不同的数字)。
马特·多尔

1
@MattDowle如果我的函数已经返回了命名列表,该如何将列添加到dt中而不必再次命名呢?例如f <-function(x){list(“ c” =“ hi”,“ d” =“ hello”)}将使用x [,f(),by = a] []打印名称为cols的结果。我不知道如何将结果附加到dt。
Jfly

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.