【问题标题】:Retain all columns in original data table when applying a function that returns a data table with multiple columns应用返回多列数据表的函数时,保留原始数据表中的所有列
【发布时间】:2020-02-18 18:12:17
【问题描述】:

假设我有一个数据表。

# Load package
library(data.table)

# Dummy data table
dt <- data.table(foo = letters[1:10], 
                 bar = LETTERS[11:20], 
                 val = 100:109)

我想在此数据表的列上运行一个函数,该函数返回一个包含多列的数据表。假设它看起来像这样:

# Function that returns data table
f <- function(x){
  data.table(stuff = paste0(x, "_stuff"),
             things = paste0("things_", x))
}

如果我在数据表中的foo 上运行它,它会返回:

# Returns only columns from function 
dt[, f(foo)]
#>       stuff   things
#>  1: a_stuff things_a
#>  2: b_stuff things_b
#>  3: c_stuff things_c
#>  4: d_stuff things_d
#>  5: e_stuff things_e
#>  6: f_stuff things_f
#>  7: g_stuff things_g
#>  8: h_stuff things_h
#>  9: i_stuff things_i
#> 10: j_stuff things_j

太棒了!现在,我希望将返回的数据表附加到我的原始数据表中。如果我想将它附加到原始数据表中的几列,我可以像这样命名我想要保留的列:

# Returns named column and columns from function
dt[, .(foo, f(foo))]
#>     foo   stuff   things
#>  1:   a a_stuff things_a
#>  2:   b b_stuff things_b
#>  3:   c c_stuff things_c
#>  4:   d d_stuff things_d
#>  5:   e e_stuff things_e
#>  6:   f f_stuff things_f
#>  7:   g g_stuff things_g
#>  8:   h h_stuff things_h
#>  9:   i i_stuff things_i
#> 10:   j j_stuff things_j

但是,我想保留 所有 列,而不必单独命名它们。一种方法是使用by:

# Retains all columns
dt[, f(foo), by = names(dt)]
#>     foo bar val   stuff   things
#>  1:   a   K 100 a_stuff things_a
#>  2:   b   L 101 b_stuff things_b
#>  3:   c   M 102 c_stuff things_c
#>  4:   d   N 103 d_stuff things_d
#>  5:   e   O 104 e_stuff things_e
#>  6:   f   P 105 f_stuff things_f
#>  7:   g   Q 106 g_stuff things_g
#>  8:   h   R 107 h_stuff things_h
#>  9:   i   S 108 i_stuff things_i
#> 10:   j   T 109 j_stuff things_j

reprex package (v0.3.0) 于 2020 年 2 月 18 日创建

这给了我这个测试用例的期望结果,但显然这只有在行是唯一的情况下才有意义。

我尝试使用dt[, .(names(dt), f(foo))] 之类的东西,但这不起作用,因为names 返回一个字符串向量,然后将其添加为列。

显而易见的解决方案是像这样使用:=dt[, c("One", "Two") := f(foo)]。这给出了所需的结果我必须自己命名添加的列,而我想保留函数返回的列名。

另一个解决方案可能是cbind(dt, dt[, foo(f)]),但这似乎很笨拙。

实现这个结果的正确方法是什么?

【问题讨论】:

  • 为什么不将cbind(dt, dt[, f(foo)]) 包装在一个函数中?
  • 当然,我可以做到,但我想知道是否有规范的数据表方法可以做到这一点,因为这一定是一项相当常见的任务。
  • dt[, names(f(dt$foo)) := f(foo)][] 之类的东西会起作用,但它并不是您真正想要的
  • @Gainz 是的,这需要f 被调用两次,这不是最优的。
  • 如果您找到更好的解决方案,请@myname。 data.table 是我用得最多的包之一,请多多学习!

标签: r data.table


【解决方案1】:

一些建议:

1) 修改函数中的data.table

f2 <- function(dt){
    cols <- c("stuff", "things")
    dt[, (cols) := lapply(paste0("_", cols), function(x) paste0(foo, x))]
}
f2(dt)

2) 使用 NSE:

eval(substitute(dt[, (LHS) := RHS], list(RHS={a <- dt[, f(foo)]}, LHS=names(a))))

3) 或者将结果分配给一个变量,然后通过引用进行更新,类似于在 cmets 中提到的 Gainz

a <- dt[, f(foo)]
dt[, names(a) := a]

我更喜欢选项 (3)。

【讨论】:

  • 同意:(3) 是最好的解决方案。
【解决方案2】:

另一种选择,如果您不需要在处理过程中对iby= 做任何事情,则将dt 作为您的函数的参数,然后在您的函数中使用set(),如图所示下面。


f <- function(x, dt){
  set(dt, j = "stuff", value = paste0(dt[[x]], "_stuff"))
  set(dt, j = "things", value = paste0("things_", dt[[x]]))
}

f("foo", dt)

输出:

> dt
    foo bar val   stuff   things
 1:   a   K 100 a_stuff things_a
 2:   b   L 101 b_stuff things_b
 3:   c   M 102 c_stuff things_c
 4:   d   N 103 d_stuff things_d
 5:   e   O 104 e_stuff things_e
 6:   f   P 105 f_stuff things_f
 7:   g   Q 106 g_stuff things_g
 8:   h   R 107 h_stuff things_h
 9:   i   S 108 i_stuff things_i
10:   j   T 109 j_stuff things_j

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-15
    • 2017-05-23
    相关资源
    最近更新 更多