【问题标题】:data.table assigning multiple columns generated as a new data.table (or list) without repeating namesdata.table 分配多个列,生成为新的 data.table(或列表)而不重复名称
【发布时间】:2019-01-22 11:15:02
【问题描述】:

我的问题是在将多个列分配给 data.table 时代码的易错性/可维护性。

我有一个返回 data.table 的函数,如下所示:

f <- function(x)
{
  # perform some complicated operations on x yielding multiple results

  data.table(col1 = my_result_1, col2 = my_result_2)
}

我正在对另一个 data.table 的行组应用此函数,并将结果添加为新列(或更新已存在的列):

dt <- data.table(x = 1 : 4, id = c(1,1,2,2))
#    x id
# 1: 1  1
# 2: 2  1
# 3: 3  2
# 4: 4  2

dt[, c('col1', 'col2') := f(x), by = id]

从技术上讲,这很好用。但是,我想知道是否有一种优雅的方法可以避免在 := 的 LHS 上再次指定 f() 中生成的列名。

【问题讨论】:

  • 这对我来说有点不清楚。通常,如果您使用:=,则必须指定要分配的列名。你如何决定新列的名称?您是否将它们存储在可以使用的某个向量中?
  • 名称在 f() 中生成的 data.table 中。我在其他任何地方都没有它们。问题是,如果我更改 f() 中的任何内容(例如向结果 data.table 添加新列),我需要确保相应地更新 f() 的调用,这是引入错误的潜在来源.
  • Fwiw,我在这个(我认为)相关问题上添加了一条评论:github.com/Rdatatable/data.table/issues/…

标签: r data.table names


【解决方案1】:

OP 要求 一种优雅的方式,避免必须在 := 的 LHS 上再次指定 f() 中生成的列名。

下面的方法既不优雅也不高效,但它避免了创建新的列名,并且如果修改函数以返回更多列或更改输出列名,它将起作用,因此,它解决了 的问题OP 提出的代码的易错性/可维护性

library(data.table)

f <- function(x) {
  my_result_1 <- x^2/sum(x)
  my_result_2 <- sum(x)/x
  data.table(col1 = my_result_1, col2 = my_result_2)
}

dt <- data.table(x = 1 : 4, id = c(1,1,2,2))
tmp <- dt[,  f(x), by = id]
dt[, names(tmp) := tmp]
dt[]
   x id      col1     col2
1: 1  1 0.3333333 3.000000
2: 2  1 1.3333333 1.500000
3: 3  2 1.2857143 2.333333
4: 4  2 2.2857143 1.750000

现在,让我们重新定义f()

f <- function(x) {
  my_result_1 <- x^2/sum(x)
  my_result_2 <- sum(x)/x
  my_result_3 <- max(x)/x
  data.table(c1 = my_result_1, c2 = my_result_2, c3 = my_result_3)
}

tmp <- dt[,  f(x), by = id]
dt[, names(tmp) := tmp]
dt[]
   x id      col1     col2        c1       c2       c3
1: 1  1 0.3333333 3.000000 0.3333333 3.000000 2.000000
2: 2  1 1.3333333 1.500000 1.3333333 1.500000 1.000000
3: 3  2 1.2857143 2.333333 1.2857143 2.333333 1.333333
4: 4  2 2.2857143 1.750000 2.2857143 1.750000 1.000000

警告

作为pointed out by Frank,此答案仅在f(x) 返回与dt 相同的行数时有效。

【讨论】:

  • 我不明白为什么使用names(tmp) := tmptmp 的行数可能比 dt 少(如果 uniqueN(dt, by="id") &lt; nrow(dt)f 正在聚合),对吧?
  • @Frank,一般来说,你是对的。但是 OP 的例子有dt[, c('col1', 'col2') := f(x), by = id]。因此,OP 已经确保 f(x) 返回与 dt 相同的行数。对tmp 的临时分配只是捕捉f(x) 结果中使用的列名的技巧。
  • 好的。 Fwiw,从我的阅读中引用的 OP 行并不意味着相同数量的行。尽管在我看来这是不好的做法,但人们经常以这种方式添加摘要统计信息(除了 .GRP 之类的组 ID),例如 f = length; data.table(mtcars)[, n := f(cyl), by=gear][]
  • 这在一般情况下不起作用,因为不能保证 tmp 中的行顺序与 dt 中的行顺序相对应。我认为 dt 必须按分组列排序才能获得正确的结果(不过,组内的行顺序无关紧要)。
猜你喜欢
  • 1970-01-01
  • 2015-10-21
  • 2015-05-24
  • 2015-10-20
  • 1970-01-01
  • 1970-01-01
  • 2021-05-14
  • 2016-10-09
  • 2016-04-15
相关资源
最近更新 更多