如何加快R data.table中的缺失搜索过程
我正在为缺失值处理编写一个通用函数。数据可以有Char、numeric、factor和integer类型的列。数据示例如下所示如何加快R data.table中的缺失搜索过程,r,data.table,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=
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[miss5 %in% c(NA,'','.'),flag:=1]
但是在一个600万的记录集上,运行这个程序需要将近1秒的时间
dt[!nzchar(miss5),flag:=1] takes close 0.14 secod to run.
我的问题是,我们是否可以有一个代码,其中花费的时间尽可能少,而我们可以查找丢失的值NA、blank和Dot(NA、“,”)
非常感谢您的帮助。您可以循环浏览“未命中”列,并使用
set
创建相应的“标志”列
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)
}
library(data.table)#v1.9.5+
ind==
和%
中的%经过优化,可自动使用二进制搜索(新功能:自动索引)。要使用它,我们必须确保:
a) 我们使用dt[…]
而不是set()
,因为它尚未在set()
中实现
b) 当%
中指向%的RHS的SEXPTYPE高于LHS时,自动索引将重新路由到基本R以确保结果正确(因为二进制搜索总是强制RHS)。因此,对于整数列,我们需要确保只传入NA
,而不是“
或”
使用@akrun的数据,下面是代码和运行时间:
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) 然后,我们为%lookup中的i
-miss(.)%创建相应的表达式
d) 然后我们计算i
中的表达式,它将使用自动索引快速创建索引,并使用该索引使用二进制搜索快速查找匹配索引
注意:如有必要,您可以在for循环的末尾添加一个set2key(dt,NULL)
,以便在使用后立即删除创建的索引(以节省空间)
与本次跑步相比,@akrun的最快回答需要6.33秒,这是~4.2倍的加速
更新:在400万行和100列上,大约需要9.2秒。即每列约0.092秒
调用[.data.table
a 100次可能会很昂贵。当在set()
中实现自动索引时,最好比较性能。注释不用于扩展讨论;此对话已被取消。
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_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