如何在 data.table 上运行 apply?

Posted

技术标签:

【中文标题】如何在 data.table 上运行 apply?【英文标题】:How do I run apply on a data.table? 【发布时间】:2012-03-03 10:24:02 【问题描述】:

我有一个data.table,第 2 到 20 列是带空格的字符串(例如,“物种名称”)。我想同时在所有这些列上运行str_replace(),以便所有“物种名称”都变成“物种名称”。我可以这样做:

data.table(apply(as.data.frame(dt[,2:dim(dt)[2], with=F]), 2, 
                               function(x) str_replace(x," ","_") ))

或者,如果我将其保留为 data.table 对象,那么我可以一次完成这一列:

dt[,SpeciesName := str_replace(SpeciesName, " ", "_")

如何对所有第 2 列到最后的所有列执行此操作,类似于上述之一?

【问题讨论】:

【参考方案1】:

于 2015 年 11 月 24 日完全重写,以修复以前版本中的错误。

还在 2019 年 9 月 27 日添加了更多现代选项

你有几个选择。

    使用嵌入式调用处理所有目标列 lapply(),使用:= 将修改后的值分配到位。这 依赖于 := 非常方便的支持同时分配到其 LHS 上命名的多个列。

    使用for 循环逐个遍历目标列, 使用set()依次修改每一个的值。

    使用for 循环迭代多个“幼稚”调用 到[.data.table(),每一个都修改一个列。

这些方法似乎都一样快,所以你使用哪一种 主要是口味问题。 (1) 非常紧凑,并且 富有表现力。这是我最常使用的,虽然你可能会发现 (2) 更容易阅读。因为它们一次处理和修改一个列,所以 (2) 或 (3) 在您的 data.table 非常大以至于您有遇到限制的危险的罕见情况下将具有优势 由 R 会话的可用内存强加。

library(data.table)

## Create three identical 1000000-by-20 data.tables
DT1 <- data.table(1:1e6,
           as.data.table(replicate(1e6, paste(sample(letters, nr, TRUE),
                                             sample(letters, nr, TRUE)))))
cnames <- c("ID", paste0("X", 1:19))
setnames(DT1, cnames)
DT2 <- copy(DT1); DT3 <- copy(DT1)

## Method 1
system.time(
DT1[, .SDcols=cnames[-1L], cnames[-1L] := 
  lapply(.SD, function(x) gsub(" ", "_", x, fixed=TRUE)), ]
)
##   user  system elapsed 
##  10.90    0.11   11.06 

## Method 2
system.time(
    for(cname in cnames[-1]) 
        set(DT2, j=cname, value=gsub(" ", "_", DT2[[cname]], fixed=TRUE))
    
)
##   user  system elapsed 
##  10.65    0.05   10.70 

## Method 3
system.time(
    for(cname in cnames[-1]) 
        DT3[ , (cname) := gsub(" ", "_", get(cname), fixed=TRUE)]
    
)
##   user  system elapsed 
##  10.33    0.03   10.37 

有关set():= 的更多详细信息,请阅读他们的帮助页面,通过键入?set?":=" 获得。

【讨论】:

这是一个有趣的案例。在这里,20 列中有 19 列正在被替换; := 的 RHS 几乎是所有表格。 := 的优势在例如将一或两列添加到 20 或修改一或两列 20 时更大。在这些情况下,大部分列都保留在原位,:= 比复制整个表要快得多。 另外,set() 是 v1.8.0 中的一个新功能(还没有在 CRAN 上),它直接提供了:= 功能。在循环内分配时,set() 可以比:= 快得多(以便更自然地编程)。主页上的最新新闻中有一个例子。 @MatthewDowle -- 感谢您的 cmets。他们提醒我,在周末,我对我在这里给出的答案有一种唠叨的感觉,并促使我重新审视这个问题。显然,我有充分的理由感到被唠叨。请看看我修改后的答案。另外请将您在 cmets 中获得的任何建议添加到我的回答文本中,您认为它们可能会有所帮助。我会看看set(),但还没有资格讨论它。再次感谢您为继续开发 data.table 包所做的所有工作! 啊,那更好 :-) 如果我可以 +2,我会的。在 := 的 RHS 上,也许可以尝试使用 DT1[[cname]] 而不是 DT1[,cname,with=FALSE][[ 不会复制(直到写入),因此在整列提取时应该比 [.data.table 快(未经测试的猜测)。 @Hack-R nr=20 解密 cmets【参考方案2】:

你可以这样做:

library("stringr")
dt[, -1] <- lapply(dt[, -1], function(x) str_replace(x," ","_"))

【讨论】:

以上是关于如何在 data.table 上运行 apply?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 R 中迭代地过滤列表中的列表或如何同时使用两个条件过滤 data.table,在运行时创建对象

如何在 Vuetify v-data-table 上对齐标题

从R中的data.table中删除带有NA的行[重复]

R data.table 从剪贴板读取

如何在 terraform 中运行 kubectl apply 命令

使用带有R内核的jupyter笔记本,如何通过引用来抑制打印结果更新data.table?