微创标记在R中修改data.table函数

时间:2017-11-17 17:17:31

标签: r data.table microbenchmark

我正在尝试比较不同的值替换函数速度,但是当我包含data.tableset():=函数时,我很难将它们设置到公平的竞争环境中。

我设置了我的微基准测试,让每个函数在本地修改数据框,这样我就不需要每次都重新创建一个新的数据帧。像这样:

# create replicable dataframe filled with 20% NAs and numeric values
set.seed(42)
Book1 <- 
    as.data.frame(matrix(sample(c(NA, runif(2, min = 1, max = 4)),   
                         3e6*10, replace=TRUE),  
                  dimnames = list(NULL, paste0("var", 1:3)), ncol=3))

看起来像:

> str(Book1)
'data.frame': 10000000 obs. of  3 variables:
$ var1: num  NA 3.81 3.74 3.74 3.81 ...
$ var2: num  3.74 3.81 NA NA 3.81 ...
$ var3: num  NA 3.74 3.81 3.74 NA ...

然后,当我在local()或其他函数中包装时间时,我可以为每个试验的每个函数重新重建原始数据框,这些函数在用NA替换0时努力工作第

例如:

# Base R functions (from alexis_laz)
baseR.for.loop = function(x) { 
    for(j in 1:ncol(x))
        x[[j]][is.na(x[[j]])] = 0
}
> system.time({local(baseR_for(Book1))})
user  system elapsed 
0.28    0.14    0.42 

,数据框保持不变。

> str(Book1)
'data.frame': 10000000 obs. of  3 variables:
$ var1: num  NA 3.81 3.74 3.74 3.81 ...
$ var2: num  3.74 3.81 NA NA 3.81 ...
$ var3: num  NA 3.74 3.81 3.74 NA ...

现在,当我运行就地修改data.table函数来执行相同操作时,即使我将操作包装在local() 中,原始数据框也会被修改一个function()

library(data.table)
DT.set.nms = function(DT) {
    for (j in names(DT))
        set(DT,which(is.na(DT[[j]])),j,0)
}
> system.time({local(DT.set.nms(Book1))})
user  system elapsed 
0.14    0.00    0.14 
> str(Book1)
'data.frame': 10000000 obs. of  3 variables:
$ var1: num  0 3.81 3.74 3.74 3.81 ...
$ var2: num  3.74 3.81 0 0 3.81 ...
$ var3: num  0 3.74 3.81 3.74 0 ...

通过更多的研究,我发现奇怪的行为是由data.table过度分配内存和修改到位功能引起的。虽然这些实际上非常强大,特别是因为我没有想到这在R中是可能的,但它们对我目前的微基准测试方法并没有太大帮助。)

所以我最初的问题是:

如何在microbenchmark中并排运行一套功能,并让所有功能都在一个公平的竞争环境中运行?

这就是我进行这些NA替换分析的方式(以及我通常如何看待微基准测试)。

library(microbenchmark)
perf_results <- microbenchmark(
    baseR_for        = local(baseR.for.loop(Book1)),
    baseR.replace    = local(replace(Book1, is.na(Book1), 0)),
    baseR.sbst.rssgn = local(Book1[is.na(Book1)] <- 0),
    times = 5L
)
> print(perf_results)
Unit: milliseconds
expr       min        lq      mean    median        uq       max neval
baseR_for  423.1889  464.4622  569.9093  636.3708  648.8386  676.6857     5
baseR.replace 1113.9829 1204.0874 1215.1211 1212.5199 1214.8138 1330.2012     5
baseR.sbst.rssgn 1156.9010 1161.4675 1262.6653 1218.1743 1360.4346 1416.3490     5
     

并且数据框保持不变。

> str(Book1)
'data.frame': 10000000 obs. of  3 variables:
$ var1: num  NA 3.81 3.74 3.74 3.81 ...
$ var2: num  3.74 3.81 NA NA 3.81 ...
$ var3: num  NA 3.74 3.81 3.74 NA ...

如果我将数据表函数添加到分析中,它们会在第一次传递到DT函数后“销毁”原始数据帧。因此,为了解决这个问题,我正在考虑每次都包含一个复制原始数据集的操作,因此每个函数仅在复制上起作用。另外,我认为我必须在自己的自定义函数中包装每个方法,以使自定义函数不再成为混淆变量。这看起来像:

baseR.sbst.rssgn <- function(x) { x[is.na(x)] <- 0; x }
baseR.replace <- function(x) { replace(x, is.na(x), 0)}

perf_results <- microbenchmark(
    baseR.for.loop   = baseR_for(copy(Book1)),
    baseR.replace    = baseR.replace(copy(Book1)),
    baseR.sbst.rssgn = baseR.sbst.rssgn(copy(Book1)),
    DT.set.nms       = DT.set.nms(copy(Book1)),
    times = 5L
)

这似乎有用,数据框保持不变,但我仍然想知道......
是否有更好的方法在data.table修改到位函数上执行微基准测试?

0 个答案:

没有答案