【问题标题】:Apply an element-wise function to data.table columns将逐元素函数应用于 data.table 列
【发布时间】:2016-10-19 20:25:58
【问题描述】:

我有一个 data.table,我想对某些列应用一个函数(例如粘贴、求和)。例如:

library(data.table)
x = as.data.table(iris)
columnsSelected = c("Sepal.Width", "Sepal.Length")
x[, A := Sepal.Width + Sepal.Length] # this yields the desired result, but not in the way I want.
x[, A := sum(columnsSelected)] # this does not execute
x[, A := sum(get(columnsSelected))] # this does not yield the desired result
x[, A := sum(.SD), .SDcols = columnsSelected] # this does not yield the desired result
x[, A := do.call(sum, .SD), .SDcols = columnsSelected] # same as above

我很茫然。建议使用Get() here,但它似乎不适用于多列。另一个关于 data.table 使用的有趣讨论是 here,但 .SD.SDcols 似乎只适用于聚合。

任何帮助将不胜感激。

【问题讨论】:

  • 你能描述一下你想要的结果是什么吗?不清楚您所说的“这会产生预期的结果,但不是我想要的方式”是什么意思

标签: r data.table


【解决方案1】:

如果您想要一个元素明智的加法,那么只需 + 而不是 sum 将向量的所有元素加在一起。此外,由于+ 只接受两个参数,所以不能使用超过两列的do.call,而需要Reduce()

x[, A := Reduce("+", .SD), .SDcols = columnsSelected]

#      Sepal.Length Sepal.Width Petal.Length Petal.Width   Species   A
#   1:          5.1         3.5          1.4         0.2    setosa 8.6
#   2:          4.9         3.0          1.4         0.2    setosa 7.9
#   3:          4.7         3.2          1.3         0.2    setosa 7.9
#   4:          4.6         3.1          1.5         0.2    setosa 7.7
#   5:          5.0         3.6          1.4         0.2    setosa 8.6
# ---                                                                
# 146:          6.7         3.0          5.2         2.3 virginica 9.7
# 147:          6.3         2.5          5.0         1.9 virginica 8.8
# 148:          6.5         3.0          5.2         2.0 virginica 9.5
# 149:          6.2         3.4          5.4         2.3 virginica 9.6
# 150:          5.9         3.0          5.1         1.8 virginica 8.9

正如@42 所评论的,我们通过将columnsSelected 变量扩展到三列来了解do.call()Reduce() 如何使用+paste() 等函数:

columnsSelected = c("Sepal.Width", "Sepal.Length", "Petal.Length")

do.call("+", ...) 给出了预期的错误,因为“+”是一个二元运算符并且不接受两个以上的参数,而do.call() 给它三个参数(columnsSelected 中的三列;@ 987654336@ 按预期工作,因为Reduce() 逐列添加,而不是像do.call 那样一次将所有列传递给函数。对于pastedo.call()Reduce() 都可以工作,因为paste() 可以接受两个或多个向量作为参数。在这种情况下,do.call() 在您有很多列时更有效,但如果您只传入两列或三列,则在这里应该无关紧要。

x[, A := do.call(paste, .SD), .SDcols = columnsSelected]

#      Sepal.Length Sepal.Width Petal.Length Petal.Width   Species           A
#   1:          5.1         3.5          1.4         0.2    setosa 3.5 5.1 1.4
#   2:          4.9         3.0          1.4         0.2    setosa   3 4.9 1.4
#   3:          4.7         3.2          1.3         0.2    setosa 3.2 4.7 1.3
#   4:          4.6         3.1          1.5         0.2    setosa 3.1 4.6 1.5
#   5:          5.0         3.6          1.4         0.2    setosa   3.6 5 1.4
# ---                                                                        
# 146:          6.7         3.0          5.2         2.3 virginica   3 6.7 5.2
# 147:          6.3         2.5          5.0         1.9 virginica   2.5 6.3 5
# 148:          6.5         3.0          5.2         2.0 virginica   3 6.5 5.2
# 149:          6.2         3.4          5.4         2.3 virginica 3.4 6.2 5.4
# 150:          5.9         3.0          5.1         1.8 virginica   3 5.9 5.1

x[, A := Reduce(paste, .SD), .SDcols = columnsSelected] 的结果与do.call() 相同。

更新: 要将参数传递给do.call()Reduce 中的函数,do.call() 将列表参数中的参数展平并将它们视为单独的,因此为了将参数传递给do.call() 中的函数,我们可以连接列表参数的命名参数,即:

x[, A := do.call(paste, c(sep = ",", .SD)), .SDcols = columnsSelected]

#      Sepal.Length Sepal.Width Petal.Length Petal.Width   Species           A
#   1:          5.1         3.5          1.4         0.2    setosa 3.5,5.1,1.4
#   2:          4.9         3.0          1.4         0.2    setosa   3,4.9,1.4
#   3:          4.7         3.2          1.3         0.2    setosa 3.2,4.7,1.3
#   4:          4.6         3.1          1.5         0.2    setosa 3.1,4.6,1.5
#   5:          5.0         3.6          1.4         0.2    setosa   3.6,5,1.4
# ---                                                                        
# 146:          6.7         3.0          5.2         2.3 virginica   3,6.7,5.2
# 147:          6.3         2.5          5.0         1.9 virginica   2.5,6.3,5
# 148:          6.5         3.0          5.2         2.0 virginica   3,6.5,5.2
# 149:          6.2         3.4          5.4         2.3 virginica 3.4,6.2,5.4
# 150:          5.9         3.0          5.1         1.8 virginica   3,5.9,5.1

另一方面,对于Reduce(),它允许你重构一个匿名函数,你可以创建一个带有sep参数的自定义函数:

x[, A := Reduce(function(x,y) paste(x,y,sep=","), .SD), .SDcols = columnsSelected]

再次给出与do.call() 相同的结果。

【讨论】:

  • 直到我将"paste" 替换为"+" 之前,我不确定这是否能回答问题。并且还将论点扩展到.SDcols
  • @42 对。一开始就试图修复他的代码。但是这个问题有点宽泛,当 OP 没有指定它试图使用哪个函数时。
  • 非常感谢您这么详细的回复!如果我想使用paste,我怎么能传递一个额外的参数,比如sep
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-05-12
  • 2019-04-03
  • 2012-05-20
  • 1970-01-01
  • 2015-08-23
  • 2018-11-10
  • 2015-07-08
相关资源
最近更新 更多