2017-04-26 95 views
2

我在R中有一個大型數據集(說> 40,000行和> 20個分類列),我反覆進行子集化處理,所以我想盡可能加快速度。它需要是一個通用函數(每個分類列有不同數量的可能值,比如字符串格式)。用多種條件在R中對子類別數據進行子集化的快速方法

每次I子集,我需要確定滿足多個邏輯集成員條件(例如> 10個條件)的行的子集。即,我需要檢查幾列,並檢查該列中的值是否與特定的成員資格相匹配(因此使用%in%)。

# simple dataset example 
library(dplyr) 
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)) 

我看了看周圍的互聯網和很多,但沒有找到我正在尋找的性能的討論。我主要使用dplyr進行編碼,所以如果在data.table有明顯的性能改善,請致歉;我已經嘗試了兩個簡單的基準測試(但沒有使用任何data.table索引等),如果速度更快,這並不明顯。

我考慮的實例中選擇(因爲我不是在data.table很大,我已經排除從這裏data.table選項):

base_filter <- function(dat) { 
    for (i in 1:7) { 
    col_name <- paste0('col', i) 
    dat <- dat[dat[[col_name]] %in% sample(letters[1:10], size = 4), ] 
    } 
    dat 
} 
dplyr_filter1 <- function(dat) { 
    for (i in 1:7) { 
    col_name <- paste0('col', i) 
    dat <- filter_(dat, 
        .dots = interp(~ colname %in% vals, 
          colname = as.name(col_name), 
          vals = sample(letters[1:10], size = 4))) 
    } 
    dat 
} 
dplyr_filter2 <- function(dat) { 
    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 = sample(letters[1:10], size = 4)) 
    } 
    filter_(dat, .dots = dots_filter) 
} 

注意:在實踐中,我的真實數據集,dplyr_filter2實際工作最快的。我也試過dtplyr或將我的數據轉換爲data.table,但這看起來比沒有慢。
注意:另一方面,實際上,當數據的行數少,列數少(可能是由於複製速度?)時,基本R函數的性能優於dplyr示例。

因此,我想問一下,在多個(集合成員資格)條件下對分類數據框進行子集化的一般,最有效的方法是什麼。如果可能的話,解釋爲什麼?對於較小的數據集,這個答案是否有區別它取決於複製時間還是搜索時間?

有用相關鏈接

+0

你爲什麼要使用樣本()?看起來這會影響你的基準測試。 – Elin

回答

2

要知道你不喜歡使用data.table。只是提供一些時間以供參考。通過索引,可以更快地執行子集化,並且可以在data.table中輕鬆完成2個表的內部連接。

# 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 
+0

對於> 20列,「dtSelection」對象不會太大以至於無法放入內存('nrow(dtSelection)== prod(length(selection))')? –

+0

@alexis_laz是很可能取決於使用情況 – chinsoon12

1

除了chinsoon12的替代品,一個要考慮的就是避免在子集化每次迭代data.frame。所以,代替

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, ]) 
} 

,或者另一種選擇,是迭代地減少比較的量,而是通過減少行索引而不是數據。框架本身:

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 
相關問題