如何在 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 上对齐标题