我正在尝试比较不同的值替换函数速度,但是当我包含data.table
个set()
和:=
函数时,我很难将它们设置到公平的竞争环境中。
我设置了我的微基准测试,让每个函数在本地修改数据框,这样我就不需要每次都重新创建一个新的数据帧。像这样:
# 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
修改到位函数上执行微基准测试?