【问题标题】:Assign multiple columns using := in data.table, by group使用 := 在 data.table 中按组分配多列
【发布时间】:2012-07-25 16:56:01
【问题描述】:

使用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]

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

【问题讨论】:

  • 这个好像已经回答了:stackoverflow.com/questions/11308754/…
  • Alex,这个答案很接近,但它似乎不能与by 结合使用,因为@Christoph_J 的说法是正确的。链接到您的问题添加到FR#2120“删除需要=FALSE for LHS of :=,因此不会忘记重新访问。
  • 明确地说,f() 是一个返回多个值的函数,每个列都有一个值。

标签: r dataframe data.table variable-assignment colon-equals


【解决方案1】:

这现在适用于 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

【讨论】:

  • 感谢您的回答和示例。我应该如何修改以下行以便从昏暗的输出中为每个 objectName 获取两列,而不是一列有两行? data.table(objectName=ls())[,c("rows","cols"):=dim(get(objectName)),by=objectName](我正在使用data.table 1.8.11)
  • @dnlbrky dim 返回一个向量,因此将其转换为类型 list 应该旋转它;例如[,c("rows","cols"):=as.list(dim(get(objectName))),by=objectNa‌​me]。麻烦的是as.list 有调用开销并且还复制了小向量。如果随着组数的增加效率成为问题,请告诉我们。
  • 嗨,马特。您的第二个代码块(即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] 这样的分配实际上应该有效?似乎这与其他更改一致,尽管我猜它可能会破坏太多现有的用户代码(?)。
  • @PanFrancisco 没有by=a 它将起作用,但返回不同的答案。 mean(a)sum(a) 聚合在 by=a 时在每个组内被回收。如果没有by=a,它只会将整列的meansum 粘贴到每个单元格中(即不同的数字)。
  • @MattDowle 如果我的函数已经返回命名列表怎么办,无论如何我可以将列添加到 dt 而不必再次命名它们?例如f
【解决方案2】:

以下速记符号可能有用。所有功劳归Andrew Brooks, specifically this article

dt[,`:=`(avg=mean(mpg), med=median(mpg), min=min(mpg)), by=cyl]

【讨论】:

    猜你喜欢
    • 2020-01-23
    • 2012-11-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-10
    相关资源
    最近更新 更多