【问题标题】:Apply a custom function on an entire column of data.table?在 data.table 的整个列上应用自定义函数?
【发布时间】:2014-02-12 17:37:45
【问题描述】:

我有一个很大的Data Table,有两列。我希望在特定列上应用自定义函数。产生问题的代码如下:

require(data.table)
X <- rep("This is just random text", 1e5)
data <- data.frame(1:1e5, replicate(1, X, simplify=FALSE), stringsAsFactors=FALSE)
colnames(data) <- paste("X", seq_len(ncol(data)), sep="")
DT <- as.data.table(data)

现在,我们有一个大数据表,看起来像

| X1 |            X2           |
|----|-------------------------|
| 1  | This is just random text|
| 2  | This is just random text|
| 3  | This is just random text|
| 4  | This is just random text|
| .. |            ...          |

如果考虑到这个 data.table 将非常大(大约~100M 行),我想对这个列的任何一个做一些向量操作怎么办。

我们以 X1 列为例。假设,我想对其应用以下功能:

Fun4X1 <- function(x){return(x+x*2)}

X2 列上有一个非常复杂的 NLP 函数,看起来像

Fun4X2 <- function(x){
             require(stringr)
             return(str_split(x, " ")[[1]][1])
          }

我应该如何为大型数据集执行此操作?请建议分钟。耗时的方法,因为我的 Function 本身就非常复杂。

附:我已经尝试过foreach、sapply,当然还有for-loop,在一个相当不错的硬件系统上它们都非常慢。

【问题讨论】:

  • 使用Fun4X2[DT$X2]有什么问题?
  • 另外,如果您正在处理文本,请熟悉 grep 和 gsub - stringr 函数是这些非常强大的函数的简单包装。
  • DT[, list(Fun4X1(X1), Fun4X3(X2))] 怎么样,其中“Fun4X3”定义为:Fun4X3 &lt;- function(x) strsplit(x, " ")[[1]][1]
  • 我没有注意,只是摆脱了您的 stringr 代码,因为我知道它要慢得多。试试这个:Fun4X3 &lt;- function(x) vapply(strsplit(x, " "), function(y) y[1], character(1))。这也应该返回一个向量,而不是一个列表。
  • @AnandaMahto,对于这个正则表达式,fixed=TRUE 也可以更快。

标签: r function data.table apply large-data


【解决方案1】:

该方法应该与将任何其他内置(或包加载)函数应用于data.table 中的特定列没有什么不同:使用list(fun(variable), otherfun(othervariable)) 类型的构造。如果需要,您还可以命名结果列,否则它们将被命名为“V1”、“V2”等等。

换句话说,对于您的问题,您可以这样做:

DT[, list(X1 = Fun4X1(X1), X2 = Fun4X2(X2))]

但是,我怀疑您的很多减速可能是由于您实际使用的功能。比较以下细微的改进:

Fun4X2.old <- function(x){
  require(stringr)
  return(str_split(x, " ")[[1]][1])
}

Fun4X2.new1 <- function(x) {
  vapply(strsplit(x, " "), 
         function(y) y[1], character(1))
} 

Fun4X2.new2 <- function(x) {
  vapply(strsplit(x, " ", fixed=TRUE), 
         function(y) y[1], character(1))
} 

Fun4X2.sub <- function(x) sub("(.+?) .*", "\\1", x)

X <- rep("This is just random text", 1e5)    

system.time(out1 <- Fun4X2.old(X))
#    user  system elapsed 
#  18.838   0.000  18.659 
system.time(out2 <- Fun4X2.new1(X))
#    user  system elapsed 
#   0.000   0.000   0.944 
system.time(out3 <- Fun4X2.new2(X))
#    user  system elapsed 
#   1.584   0.000   0.270 
system.time(out4 <- Fun4X2.sub(X))
#    user  system elapsed 
#   0.000   0.000   0.222 

最后一点,关于your comment here:

@AnandaMahto 我正在寻找与此类似的内容,但如果我使用您的解决方案,则文本列上的输出未矢量化,即使我在每一行中有不同的文本,我也会得到相同的输出

顺便说一句,您原来的Fun4X2()(上面重命名为Fun4X2.old())表现出相同的行为。

DT2 <- data.table(X1 = 1:4, X2 = c("a b c", "d e f", "g h i", "j k l"))
DT2[, list(Fun4X1(X1), Fun4X2.old(X2))]
#    V1 V2
# 1:  3  a
# 2:  6  a
# 3:  9  a
# 4: 12  a

DT2[, list(Fun4X1(X1), Fun4X2.new1(X2))]
#    V1 V2
# 1:  3  a
# 2:  6  d
# 3:  9  g
# 4: 12  j

【讨论】:

  • 感谢@Ananda 的解决方案。只需1个快速怀疑;我们可以采用这个解决方案并同时在多个内核中处理它吗?我的意思是,如果我有 4 个核心和 100000 行,那么我可以将数据划分为每个核心 25k 行并处理它然后再次组合吗?因为它可以将速度提高近 4 倍。此外,第 1 列可以作为键,第 2 列可以作为值。
【解决方案2】:

第二个问题可以使用快速矢量化函数sub:

Fun4X2 <- function(x) sub("(.+?) .*", "\\1", x)

head(Fun4X2(DT[,X2]))
# [1] "This" "This" "This" "This" "This" "This"

【讨论】:

  • 这确实是 the fastest option 对于这部分问题,如果它是 OP 实际问题的一部分。为此 +1 :-)
【解决方案3】:

查看降雪包http://cran.r-project.org/web/packages/snowfall/snowfall.pdf,用于并行计算。您可以设置本地集群并利用所有核心。我发现通过使用这个包中的sfApply,它已经将我的大部分计算时间减少了 5 倍

(我有一个 8 核,所以它会快 8 倍,但显然有将数据加载到集群中并在最后收集它的成本)。

例如

install.packages('snowfall')

require(snowfall)
sfInit( parallel=TRUE, cpus=4 )
sfExport(list=c('DT','Fun4X1','Fun4X2'))
sfApply(DT,1,function(X) return(c(fun4X1(X[1]),fun4X2(X[2]))))
sfStop()

apply 在我的机器上需要 25.07 秒,sfApply 需要 9.11 秒

【讨论】:

  • 感谢@James 的努力。我用@Ananda 给出的方法尝试了你的解决方案,我正在使用一些函数,比如 NLP 包中的 as.String 以及 Fun4X2 中的更多函数,所以似乎有一些错误,比如 Error in checkForRemoteErrors(val) : 4 nodes produced errors; first error: could not find function "as.String"
  • 通过调用函数本身内部的那些包解决了错误...谢谢...将在大数据上尝试新的解决方案并尽快发布反馈和组合方法...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-07-29
  • 2021-10-09
  • 1970-01-01
  • 1970-01-01
  • 2018-05-02
  • 2023-04-04
  • 1970-01-01
相关资源
最近更新 更多