att*_*ool 30 r data.table
我试图通过应用共享函数找出一种优雅的方法来使用:=赋值来一次替换多列data.table.这种情况的典型用法可能是将字符串函数(例如gsub)应用于表中的所有字符列.将data.frame这样做的方式扩展到a 并不困难data.table,但我正在寻找一种与data.table做事方式一致的方法.
例如:
library(data.table)
m <- matrix(runif(10000), nrow = 100)
df <- df1 <- df2 <- df3 <- as.data.frame(m)
dt <- as.data.table(df)
head(names(df))
head(names(dt))
## replace V20-V100 with sqrt
# data.frame approach
# by column numbers
df1[20:100] <- lapply(df1[20:100], sqrt)
# by reference to column numbers
v <- 20:100
df2[v] <- lapply(df2[v], sqrt)
# by reference to column names
n <- paste0("V", 20:100)
df3[n] <- lapply(df3[n], sqrt)
# data.table approach
# by reference to column names
n <- paste0("V", 20:100)
dt[, n] <- lapply(dt[, n, with = FALSE], sqrt)
Run Code Online (Sandbox Code Playgroud)
我知道使用:=to assign 循环遍历列名的向量更有效:
for (col in paste0("V", 20:100)) dt[, col := sqrt(dt[[col]]), with = FALSE]
Run Code Online (Sandbox Code Playgroud)
我不喜欢这个,因为我不喜欢data.table在j表达式中引用它.我也知道我可以使用:=赋值lapply我知道列名:
dt[, c("V20", "V30", "V40", "V50", "V60") := lapply(list(V20, V30, V40, V50, V60), sqrt)]
Run Code Online (Sandbox Code Playgroud)
(您可以通过构建具有未知列名称的表达式来扩展它.)
以下是我在此尝试的想法,但我无法让他们工作.我犯了错误,还是有其他方法我错过了?
# possible data.table approaches?
# by reference to column names; assignment works, but not lapply
n <- paste0("V", 20:100)
dt[, n := lapply(n, sqrt), with = FALSE]
# by (smaller for example) list; lapply works, but not assignment
dt[, list(list(V20, V30, V40, V50, V60)) := lapply(list(V20, V30, V40, V50, V60), sqrt)]
# by reference to list; neither assignment nor lapply work
l <- parse(text = paste("list(", paste(paste0("V", 20:100), collapse = ", "), ")"))
dt[, eval(l) := lapply(eval(l), sqrt)]
Run Code Online (Sandbox Code Playgroud)
Mat*_*wle 37
是的,你在这里是对的:
我知道使用
:=to assign 循环遍历列名的向量更有效:
for (col in paste0("V", 20:100)) dt[, col := sqrt(dt[[col]]), with = FALSE]
旁白:请注意,这样做的新方法是:
for (col in paste0("V", 20:100))
dt[ , (col) := sqrt(dt[[col]])]
Run Code Online (Sandbox Code Playgroud)
因为with = FALSE它不易被读取,无论它是指LHS还是RHS :=.放在一边.
如您所知,这是有效的,因为每列都会逐一进行,因此一次只需要一列工作内存.这可以在它工作之间产生差异,并且由于可怕的内存不足错误而失败.
lapplyRHS 的问题在于首先评估:=RHS(the lapply); 即,创建了80列的结果.这是80列的新内存,必须分配和填充.因此,您需要80列的空闲RAM才能成功完成该操作.RAM的使用占主导地位,后来将这80个新列分配(plonking)到data.table的列指针槽中.
正如@Frank指出的那样,如果你有很多列(比如10,000或更多),那么派遣到该[.data.table方法的小开销就会加起来).以消除开销,有data.table::set其下?set被描述为"加入影片箱" :=.我for为这种类型的操作使用循环.这是最快的方式,并且相当容易编写和阅读.
for (col in paste0("V", 20:100))
set(dt, j = col, value = sqrt(dt[[col]]))
Run Code Online (Sandbox Code Playgroud)
虽然只有80列,但它不太重要.(注意,循环set遍历大量行而不是大量列可能更常见.)但是,循环set不能解决重复引用dt您在问题中提到的符号名称的问题:
我不喜欢这个,因为我不喜欢在aj表达式中引用data.table.
同意.所以我能做的最好的事情是恢复你的循环,:=但get改为使用.
for (col in paste0("V", 20:100))
dt[, (col) := sqrt(get(col))]
Run Code Online (Sandbox Code Playgroud)
但是,我担心使用getin j可能效率低下.需要进行基准测试,#1380.此外,也许get()在RHS上使用而不是在LHS上使用会令人困惑.为了解决这个问题,我们可以为LHS加糖并允许get(),#1381:
for (col in paste0("V", 20:100))
dt[, get(col) := sqrt(get(col))]
Run Code Online (Sandbox Code Playgroud)
此外,或许value的set可能的范围内运行DT,#1382.
for (col in paste0("V", 20:100))
set(dt, j = col, value = sqrt(get(col))
Run Code Online (Sandbox Code Playgroud)
edd*_*ddi 14
如果要按字符串名称引用列,这些应该有效:
n = paste0("V", 20:100)
dt[, (n) := lapply(n, function(x) {sqrt(get(x))})]
Run Code Online (Sandbox Code Playgroud)
要么
dt[, (n) := lapply(n, function(x) {sqrt(dt[[x]])})]
Run Code Online (Sandbox Code Playgroud)
这是你想要的?
dt[ , names(dt)[20:100] :=lapply(.SD, function(x) sqrt(x) ) , .SDcols=20:100]
Run Code Online (Sandbox Code Playgroud)
我听说告诉大家,使用.SD不是很有效率,因为它使表的副本事前,但如果你的表是不是巨大的,我怀疑它将使太大的差别(当然这取决于你的系统规格这是相对的).