多条件R中分类数据子集的快速方法

多条件R中分类数据子集的快速方法,r,performance,dplyr,subset,R,Performance,Dplyr,Subset,我在R中有一个大的数据集(比如说>40000行和>20个分类列),我重复地将其子集,所以我希望尽可能地加快这一速度。它需要是一个通用函数(每个分类列都有离散数量的可能值,比如字符串格式) 每次创建子集时,我都需要确定满足多个逻辑集成员条件(例如>10个条件)的行子集。也就是说,我需要检查几列,并检查该列中的值是否与某个集合成员身份匹配(因此在%中使用了% 注意:实际上,在我的真实数据集上,dplyr\u filter2实际上工作得最快。我还尝试了dtplyr或将我的数据转换为data.table

我在R中有一个大的数据集(比如说>40000行和>20个分类列),我重复地将其子集,所以我希望尽可能地加快这一速度。它需要是一个通用函数(每个分类列都有离散数量的可能值,比如字符串格式)

每次创建子集时,我都需要确定满足多个逻辑集成员条件(例如>10个条件)的行子集。也就是说,我需要检查几列,并检查该列中的值是否与某个集合成员身份匹配(因此在%中使用了
%

注意:实际上,在我的真实数据集上,
dplyr\u filter2
实际上工作得最快。我还尝试了
dtplyr
或将我的数据转换为
data.table
,但这似乎比没有转换要慢。
注意:另一方面,在实践中,当数据的行数和列数较少时(可能是由于复制速度的原因),base R函数的性能优于
dplyr
示例

因此,我想问,在多个(集合成员资格)条件下,对分类数据帧进行子集的一般、最有效的方法是什么。如果可能的话,解释一下原因?对于较小的数据集,这个答案是否有所不同?这取决于复制时间还是搜索时间

有用的相关链接


了解您不喜欢使用data.table。下面只提供一些时间安排供参考。通过索引,可以更快地执行子集设置,并且还可以在
data.table中轻松完成两个表的内部联接

# simple dataset example
library(dplyr)
library(lazyeval)
set.seed(0L)
num_col <- 15
num_row <- 100000
dat_list <- list()
for (i in 1:num_col) {
    dat_list[[i]] <- data_frame(sample(letters[1:10], size = num_row, r = T))
}
dat <- bind_cols(dat_list)
names(dat) <- paste0("col", seq(15))

selection <- lapply(1:7, function(n) sample(letters[1:10], size = 4))

base_filter <- function(df) {
    for (i in 1:7) {
        col_name <- paste0('col', i)
        df <- df[df[[col_name]] %in% selection[[i]], ]
    }
    df
}

dplyr_filter1 <- function(df) {
    for (i in 1:7) {
        col_name <- paste0('col', i)
        df <- filter_(df,
            .dots = interp(~ colname %in% vals,
                colname = as.name(col_name),
                vals = selection[[i]]))
    }
    df
}

dplyr_filter2 <- function(df) {
    dots_filter <- list()
    for (i in 1:7) {
        col_name <- paste0('col', i)
        dots_filter[[i]] <- interp(~ colname %in% vals,
            colname = as.name(col_name),
            vals = selection[[i]])
    }
    filter_(df, .dots = dots_filter)
}


library(data.table)

#convert data.frame into data.table
dt <- data.table(dat, key=names(dat)[1:7])

#create the sets of selection
dtSelection <- data.table(expand.grid(selection, stringsAsFactors=FALSE))


library(microbenchmark)
microbenchmark(
    base_filter(dat),
    dplyr_filter1(dat),
    dplyr_filter2(dat),
    dt[dtSelection, nomatch=0],   #perform inner join between dataset and selection
    times=5L)

#Unit: milliseconds
#                         expr       min        lq      mean    median        uq       max neval
#             base_filter(dat) 27.084801 27.870702 35.849261 32.045900 32.872601 59.372301     5
#           dplyr_filter1(dat) 23.130100 24.114301 26.922081 24.860701 29.804301 32.701002     5
#           dplyr_filter2(dat) 29.641101 30.686002 32.363681 31.103000 31.884701 38.503601     5
# dt[dtSelection, nomatch = 0]  3.626001  3.646201  3.829341  3.686601  3.687001  4.500901     5
#简单数据集示例
图书馆(dplyr)
图书馆(懒汉)
种子集(0升)

除了CHISON12的备选方案之外,还有一件事要考虑的是避免在每次迭代中对DATA帧进行子设置。因此,与其

f0 = function(x, cond)
{
    for(j in seq_along(x)) x = x[x[[j]] %in% cond[[j]], ]
    return(x)
}
一种替代方法是累积是否将每一行包括在最终子集中的逻辑向量:

f1 = function(x, cond)
{
    i = rep_len(TRUE, nrow(x))
    for(j in seq_along(x)) i = i & (x[[j]] %in% cond[[j]])
    return(x[i, ])
}
或者,另一种替代方法是迭代减少比较量,但通过减少行索引而不是data.frame本身:

f2 = function(x, cond)
{
    i = 1:nrow(x)
    for(j in seq_along(x)) i = i[x[[j]][i] %in% cond[[j]]]
    return(x[i, ])
}
并与数据进行比较:

set.seed(1821)
dat = as.data.frame(replicate(30, sample(c(letters, LETTERS), 5e5, TRUE), FALSE), 
                    stringsAsFactors = FALSE)
conds = replicate(ncol(dat), sample(c(letters, LETTERS), 48), FALSE)

system.time({ ans0 = f0(dat, conds) })
#   user  system elapsed 
#   3.44    0.28    3.86 
system.time({ ans1 = f1(dat, conds) })
#   user  system elapsed 
#   0.66    0.01    0.68 
system.time({ ans2 = f2(dat, conds) })
#   user  system elapsed 
#   0.34    0.01    0.39

identical(ans0, ans1)
#[1] TRUE
identical(ans1, ans2)
#[1] TRUE

为什么要使用sample()?这似乎会影响您的基准测试。如果列数超过20列,“dtSelection”对象是否太大而无法放入内存(
nrow(dtSelection)==prod(length(selection))
)?@alexis_-laz很可能会根据使用情况而定
f2 = function(x, cond)
{
    i = 1:nrow(x)
    for(j in seq_along(x)) i = i[x[[j]][i] %in% cond[[j]]]
    return(x[i, ])
}
set.seed(1821)
dat = as.data.frame(replicate(30, sample(c(letters, LETTERS), 5e5, TRUE), FALSE), 
                    stringsAsFactors = FALSE)
conds = replicate(ncol(dat), sample(c(letters, LETTERS), 48), FALSE)

system.time({ ans0 = f0(dat, conds) })
#   user  system elapsed 
#   3.44    0.28    3.86 
system.time({ ans1 = f1(dat, conds) })
#   user  system elapsed 
#   0.66    0.01    0.68 
system.time({ ans2 = f2(dat, conds) })
#   user  system elapsed 
#   0.34    0.01    0.39

identical(ans0, ans1)
#[1] TRUE
identical(ans1, ans2)
#[1] TRUE