我有一个很大的data.table,其中有许多缺失值分散在它的 ~200k 行和 200 列中。我想尽可能有效地将这些 NA 值重新编码为零。
我看到两个选项: 1:转换为 data.frame,并使用类似这样 的东西 2:某种很酷的 data.table 子设置命令
我会对类型 1 的相当有效的解决方案感到满意。转换为 data.frame 然后再转换回 data.table 不会花费太长时间。
这是一个使用data.table运算符的解决方案:=,基于 Andrie 和 Ramnath 的答案。
:=
require(data.table) # v1.6.6 require(gdata) # v2.8.2 set.seed(1) dt1 = create_dt(2e5, 200, 0.1) dim(dt1) [1] 200000 200 # more columns than Ramnath's answer which had 5 not 200 f_andrie = function(dt) remove_na(dt) f_gdata = function(dt, un = 0) gdata::NAToUnknown(dt, un) f_dowle = function(dt) { # see EDIT later for more elegant solution na.replace = function(v,value=0) { v[is.na(v)] = value; v } for (i in names(dt)) eval(parse(text=paste("dt[,",i,":=na.replace(",i,")]"))) } system.time(a_gdata = f_gdata(dt1)) user system elapsed 18.805 12.301 134.985 system.time(a_andrie = f_andrie(dt1)) Error: cannot allocate vector of size 305.2 Mb Timing stopped at: 14.541 7.764 68.285 system.time(f_dowle(dt1)) user system elapsed 7.452 4.144 19.590 # EDIT has faster than this identical(a_gdata, dt1) [1] TRUE
请注意,f_dowle 通过引用更新了 dt1。如果需要本地副本,则需要显式调用该copy函数来制作整个数据集的本地副本。data.table 的setkey,key<-并且:=不要写时复制。
copy
setkey
key<-
接下来,让我们看看 f_dowle 把时间花在了哪里。
Rprof() f_dowle(dt1) Rprof(NULL) summaryRprof() $by.self self.time self.pct total.time total.pct "na.replace" 5.10 49.71 6.62 64.52 "[.data.table" 2.48 24.17 9.86 96.10 "is.na" 1.52 14.81 1.52 14.81 "gc" 0.22 2.14 0.22 2.14 "unique" 0.14 1.36 0.16 1.56 ... snip ...
在那里,我将专注于na.replace和is.na,那里有一些矢量副本和矢量扫描。通过编写一个小的 na.replace C 函数可以很容易地消除这些问题,该函数NA通过向量中的引用进行更新。我认为这至少可以将 20 秒缩短一半。任何 R 包中都存在这样的功能吗?
na.replace
is.na
NA
失败的原因f_andrie可能是因为它复制了整个dt1,或者创建了一个与整个 一样大的逻辑矩阵dt1,几次。其他 2 种方法一次只处理一列(尽管我只是简要地看了一下NAToUnknown)。
f_andrie
dt1
NAToUnknown
编辑 (Ramnath 在评论中要求的更优雅的解决方案):
f_dowle2 = function(DT) { for (i in names(DT)) DT[is.na(get(i)), (i):=0] } system.time(f_dowle2(dt1)) user system elapsed 6.468 0.760 7.250 # faster, too identical(a_gdata, dt1) [1] TRUE
我希望我一开始就这样做!
EDIT2 (一年多后,现在)
还有set(). 如果有很多列被循环通过,这可能会更快,因为它避免[,:=,]了循环调用的(小)开销。set是一个可循环的:=. 见?set。
set()
[,:=,]
set
?set
f_dowle3 = function(DT) { # either of the following for loops # by name : for (j in names(DT)) set(DT,which(is.na(DT[[j]])),j,0) # or by number (slightly faster than by name) : for (j in seq_len(ncol(DT))) set(DT,which(is.na(DT[[j]])),j,0) }