【发布时间】:2020-04-14 05:39:02
【问题描述】:
原生 R 中的 which() 方法使用什么算法来搜索数据帧?例如,如果我调用
df[which(df$parameter == 3)]
R 使用什么算法来搜索这个数据框?二进制搜索?线性搜索?我在任何地方都找不到这方面的文档。如果它不使用上述任何一种,它使用什么算法,它的时间复杂度是多少?
【问题讨论】:
原生 R 中的 which() 方法使用什么算法来搜索数据帧?例如,如果我调用
df[which(df$parameter == 3)]
R 使用什么算法来搜索这个数据框?二进制搜索?线性搜索?我在任何地方都找不到这方面的文档。如果它不使用上述任何一种,它使用什么算法,它的时间复杂度是多少?
【问题讨论】:
如果你对这个问题感兴趣,可以关注the datatable vignette on the subject
默认情况下,R 使用线性搜索。它扫描整个向量。这是data.table 的一大特色,可以让binary search 使用索引表。在data.table 中,索引数据集将在内存中物理排序元素,因此在其中搜索值非常快。 data.table(见here)中的二级索引将启用二分搜索,但数据不会在连续的内存槽中物理重新排序。
也许data.table 小插图更明确:
可以看出,每次搜索我们都会将搜索次数减少一半。这就是为什么基于二进制搜索的子集非常快的原因。由于 data.tables 的每一列的行在内存中具有连续的位置,因此操作以非常缓存高效的方式执行(也有助于提高速度)。
此外,由于我们直接获得匹配的行索引,而无需创建那些巨大的逻辑向量(等于 data.table 中的行数),因此内存效率也很高。
让我们看一个例子。包microbenchmark 用于比较。
我们会比较:
data.frame
dplyr 过滤
data.table
data.table(使用setkey)data.table(使用setindex)让我们创建所需的每个部分:
library(data.table)
library(dplyr)
df <- data.frame(
x = rnorm(1e6),
y = rnorm(1e6)
)
dt <- as.data.table(df)
dt2 <- copy(dt)
dt3 <- copy(dt)
我们将在dt2上设置主键(二分查找+内存中重新排序的对象)和dt3上设置辅助键(二分查找但在内存中没有重新排序)
setkey(dt2, x)
setindex(dt3, x)
微基准测试:
m <- microbenchmark::microbenchmark(
df[df$x<0,],
df %>% dplyr::filter(x<0),
dt[x<0],
dt2[x<0],
dt3[x<0],
times = 20L
)
m
Unit: milliseconds
expr min lq mean median uq max neval
df[df$x < 0, ] 56.56557 57.54392 66.97838 61.39609 75.30391 91.42418 20
df %>% dplyr::filter(x < 0) 23.24242 24.15183 34.64290 26.02232 34.91405 143.10476 20
dt[x < 0] 18.32496 18.96585 21.35255 20.25604 23.02666 33.25656 20
dt2[x < 0] 10.85129 10.94804 11.92941 11.21601 11.80469 18.29040 20
dt3[x < 0] 18.37789 18.47568 19.51928 18.76135 19.39782 26.90826 20
到目前为止,基本 R 方法是最慢的。在此示例中,使用二级索引并没有提高那么多性能。但是主键可以!
ggplot2::autoplot(m)
【讨论】: