如何加快 R data.table 中丢失的搜索过程
How to speed up missing search process in a R data.table
我正在编写一个用于缺失值处理的通用函数。数据可以有 Char、numeric、factor 和 integer 类型的列。数据示例如下
dt<-data.table(
num1=c(1,2,3,4,NA,5,NA,6),
num3=c(1,2,3,4,5,6,7,8),
int1=as.integer(c(NA,NA,102,105,NA,300,400,700)),
int3=as.integer(c(1,10,102,105,200,300,400,700)),
cha1=c('a','b','c',NA,NA,'c','d','e'),
cha3=c('xcda','b','c','miss','no','c','dfg','e'),
fact1=c('a','b','c',NA,NA,'c','d','e'),
fact3=c('ad','bd','cc','zz','yy','cc','dd','ed'),
allm=as.integer(c(NA,NA,NA,NA,NA,NA,NA,NA)),
miss=as.character(c("","",'c','miss','no','c','dfg','e')),
miss2=as.integer(c('','',3,4,5,6,7,8)),
miss3=as.factor(c(".",".",".","c","d","e","f","g")),
miss4=as.factor(c(NA,NA,'.','.','','','t1','t2')),
miss5=as.character(c(NA,NA,'.','.','','','t1','t2'))
)
我使用这段代码来标记缺失值:
dt[,flag:=ifelse(is.na(miss5)|!nzchar(miss5),1,0)]
但结果很慢,另外我必须添加逻辑也可以考虑“。”作为失踪。
所以我打算写这个用于缺失值识别
dt[miss5 %in% c(NA,'','.'),flag:=1]
但是在 600 万条记录集上,运行 需要将近 1 秒,而
dt[!nzchar(miss5),flag:=1] takes close 0.14 secod to run.
我的问题是,我们能否有一个代码,在其中花费的时间尽可能少,同时我们可以查找值 NA、blank 和 Dot(NA,".","") 作为缺失值?
非常感谢任何帮助。
您可以遍历 'miss' 列并使用 set
创建相应的 'flag' 列。
library(data.table)#v1.9.5+
ind <- grep('^miss', names(dt))
nm1 <- sub('miss', 'flag',names(dt)[ind])
dt[,(nm1) := 0]
for(j in seq_along(ind)){
set(dt, i=which(dt[[ind[j]]] %in% c('.', '', NA)),j= nm1[j], value=1L)
}
基准
set.seed(24)
df1 <- as.data.frame(matrix(sample(c(NA,0:9), 6e6*5, replace=TRUE), ncol=5))
set.seed(23)
df2 <- as.data.frame(matrix(sample(c('.','', letters[1:5]), 6e6*5,
replace=TRUE), ncol=5))
set.seed(234)
i1 <- sample(10)
dfN <- setNames(cbind(df1, df2)[i1], paste0('miss',1:10))
dt <- as.data.table(dfN)
system.time({
ind <- grep('^miss', names(dt))
nm1 <- sub('miss', 'flag',names(dt)[ind])
dt[,(nm1) := 0L]
for(j in seq_along(ind)){
set(dt, i=which(dt[[ind[j]]] %in% c('.', '', NA)), j= nm1[j], value=1L)
}
}
)
#user system elapsed
# 8.352 0.150 8.496
system.time({
m1 <- matrix(0, nrow=6e6, ncol=10)
m2 <- sapply(seq_along(dt), function(i) {
ind <- which(dt[[i]] %in% c('.', '', NA))
replace(m1[,i], ind, 1L)})
cbind(dt, m2)})
#user system elapsed
# 14.227 0.362 14.582
==
和 %in%
已优化为自动使用二进制搜索(新功能:自动索引)。要使用它,我们必须确保:
a) 我们使用 dt[...]
而不是 set()
,因为它尚未在 set()
、#1196.
中实现
b) 当 RHS 到 %in%
的 SEXPTYPE 比 LHS 高时,自动索引重新路由到基 R 以确保正确的结果(因为二进制搜索总是强制 RHS)。因此,对于整数列,我们需要确保只传入 NA
而不是 "."
或 ""
.
使用@ak运行的数据,这里是代码和运行时间:
in_col = grep("^miss", names(dt), value=TRUE)
out_col = gsub("^miss", "flag", in_col)
system.time({
dt[, (out_col) := 0L]
for (j in seq_along(in_col)) {
if (class(.subset2(dt, in_col[j])) %in% c("character", "factor")) {
lookup = c("", ".", NA)
} else lookup = NA
expr = call("%in%", as.name(in_col[j]), lookup)
tt = dt[eval(expr), (out_col[j]) := 1L]
}
})
# user system elapsed
# 1.174 0.295 1.476
工作原理:
a) 我们首先将所有输出列初始化为 0。
b) 然后,对于每一列,我们检查它的类型并相应地创建 lookup
。
c) 然后我们为 i
- miss(.) %in% lookup
创建相应的表达式
d) 然后我们评估 i
中的表达式,它将使用 自动索引 非常快速地创建索引并使用该索引快速找到匹配的索引使用 二进制搜索.
Note: If necessary, you can add a set2key(dt, NULL)
at the end of for-loop so that the created indices are removed immediately after use (to save space).
与此 运行 相比,@ak运行 的最快答案需要 6.33 秒,即 ~4.2 倍加速。
更新: 在 400 万行和 100 列上,大约需要 9.2 秒。每列约 0.092 秒。
调用 [.data.table
100 次可能会很昂贵。 set()
实现自动索引后,比较一下性能就好了。
我正在编写一个用于缺失值处理的通用函数。数据可以有 Char、numeric、factor 和 integer 类型的列。数据示例如下
dt<-data.table(
num1=c(1,2,3,4,NA,5,NA,6),
num3=c(1,2,3,4,5,6,7,8),
int1=as.integer(c(NA,NA,102,105,NA,300,400,700)),
int3=as.integer(c(1,10,102,105,200,300,400,700)),
cha1=c('a','b','c',NA,NA,'c','d','e'),
cha3=c('xcda','b','c','miss','no','c','dfg','e'),
fact1=c('a','b','c',NA,NA,'c','d','e'),
fact3=c('ad','bd','cc','zz','yy','cc','dd','ed'),
allm=as.integer(c(NA,NA,NA,NA,NA,NA,NA,NA)),
miss=as.character(c("","",'c','miss','no','c','dfg','e')),
miss2=as.integer(c('','',3,4,5,6,7,8)),
miss3=as.factor(c(".",".",".","c","d","e","f","g")),
miss4=as.factor(c(NA,NA,'.','.','','','t1','t2')),
miss5=as.character(c(NA,NA,'.','.','','','t1','t2'))
)
我使用这段代码来标记缺失值:
dt[,flag:=ifelse(is.na(miss5)|!nzchar(miss5),1,0)]
但结果很慢,另外我必须添加逻辑也可以考虑“。”作为失踪。 所以我打算写这个用于缺失值识别
dt[miss5 %in% c(NA,'','.'),flag:=1]
但是在 600 万条记录集上,运行 需要将近 1 秒,而
dt[!nzchar(miss5),flag:=1] takes close 0.14 secod to run.
我的问题是,我们能否有一个代码,在其中花费的时间尽可能少,同时我们可以查找值 NA、blank 和 Dot(NA,".","") 作为缺失值?
非常感谢任何帮助。
您可以遍历 'miss' 列并使用 set
创建相应的 'flag' 列。
library(data.table)#v1.9.5+
ind <- grep('^miss', names(dt))
nm1 <- sub('miss', 'flag',names(dt)[ind])
dt[,(nm1) := 0]
for(j in seq_along(ind)){
set(dt, i=which(dt[[ind[j]]] %in% c('.', '', NA)),j= nm1[j], value=1L)
}
基准
set.seed(24)
df1 <- as.data.frame(matrix(sample(c(NA,0:9), 6e6*5, replace=TRUE), ncol=5))
set.seed(23)
df2 <- as.data.frame(matrix(sample(c('.','', letters[1:5]), 6e6*5,
replace=TRUE), ncol=5))
set.seed(234)
i1 <- sample(10)
dfN <- setNames(cbind(df1, df2)[i1], paste0('miss',1:10))
dt <- as.data.table(dfN)
system.time({
ind <- grep('^miss', names(dt))
nm1 <- sub('miss', 'flag',names(dt)[ind])
dt[,(nm1) := 0L]
for(j in seq_along(ind)){
set(dt, i=which(dt[[ind[j]]] %in% c('.', '', NA)), j= nm1[j], value=1L)
}
}
)
#user system elapsed
# 8.352 0.150 8.496
system.time({
m1 <- matrix(0, nrow=6e6, ncol=10)
m2 <- sapply(seq_along(dt), function(i) {
ind <- which(dt[[i]] %in% c('.', '', NA))
replace(m1[,i], ind, 1L)})
cbind(dt, m2)})
#user system elapsed
# 14.227 0.362 14.582
==
和 %in%
已优化为自动使用二进制搜索(新功能:自动索引)。要使用它,我们必须确保:
a) 我们使用 dt[...]
而不是 set()
,因为它尚未在 set()
、#1196.
b) 当 RHS 到 %in%
的 SEXPTYPE 比 LHS 高时,自动索引重新路由到基 R 以确保正确的结果(因为二进制搜索总是强制 RHS)。因此,对于整数列,我们需要确保只传入 NA
而不是 "."
或 ""
.
使用@ak运行的数据,这里是代码和运行时间:
in_col = grep("^miss", names(dt), value=TRUE)
out_col = gsub("^miss", "flag", in_col)
system.time({
dt[, (out_col) := 0L]
for (j in seq_along(in_col)) {
if (class(.subset2(dt, in_col[j])) %in% c("character", "factor")) {
lookup = c("", ".", NA)
} else lookup = NA
expr = call("%in%", as.name(in_col[j]), lookup)
tt = dt[eval(expr), (out_col[j]) := 1L]
}
})
# user system elapsed
# 1.174 0.295 1.476
工作原理:
a) 我们首先将所有输出列初始化为 0。
b) 然后,对于每一列,我们检查它的类型并相应地创建 lookup
。
c) 然后我们为 i
- miss(.) %in% lookup
d) 然后我们评估 i
中的表达式,它将使用 自动索引 非常快速地创建索引并使用该索引快速找到匹配的索引使用 二进制搜索.
Note: If necessary, you can add a
set2key(dt, NULL)
at the end of for-loop so that the created indices are removed immediately after use (to save space).
与此 运行 相比,@ak运行 的最快答案需要 6.33 秒,即 ~4.2 倍加速。
更新: 在 400 万行和 100 列上,大约需要 9.2 秒。每列约 0.092 秒。
调用 [.data.table
100 次可能会很昂贵。 set()
实现自动索引后,比较一下性能就好了。