【问题标题】:Faster way to subset on rows of a data frame in R?在 R 中对数据框的行进行子集化的更快方法?
【发布时间】:2015-02-02 21:27:11
【问题描述】:

我一直在交替使用这两种方法来从 R 中的数据框中子集数据。
方法一
subset_df <- df[which(df$age>5) , ]
方法二
subset_df <- subset(df, age>5)

我有 2 个属于这些的问题。
1. 考虑到我有非常大的数据,哪一个更快?
2. 此处Subsetting data frames in R 的帖子表明上述两种方法之间实际上存在差异。其中之一准确地处理 NA。那么哪一种可以安全使用呢?

【问题讨论】:

  • 对于 (1) 尝试使用包 microbenchmark 运行一些基准测试
  • 如果您担心大型数据集的速度,您可能应该使用dplyrdata.table(或dplyr 作为data.table 的前端)。跨度>
  • 使用[ 对编程也很安全,而subset 是一个仅用于交互使用的便利功能。有关更多信息,请参阅上面的链接。

标签: r dataframe subset


【解决方案1】:

该问题要求以更快的方式对数据框的行进行子集化。最快的方法是使用 data.table。

set.seed(1)  # for reproducible example
# 1 million rows - big enough?
df <- data.frame(age=sample(1:65,1e6,replace=TRUE),x=rnorm(1e6),y=rpois(1e6,25))

library(microbenchmark)
microbenchmark(result<-df[which(df$age>5),],
               result<-subset(df, age>5), 
               result<-df[df$age>5,],
               times=10)
# Unit: milliseconds
#                               expr       min        lq    median       uq      max neval
#  result <- df[which(df$age > 5), ]  77.01055  80.62678  81.43786 133.7753 145.4756    10
#      result <- subset(df, age > 5) 190.89829 193.04221 197.49973 203.7571 263.7738    10
#         result <- df[df$age > 5, ] 169.85649 171.02084 176.47480 185.9394 191.2803    10

library(data.table)
DT <- as.data.table(df)     # data.table
microbenchmark(DT[age > 5],times=10)
# Unit: milliseconds
#         expr      min       lq  median       uq      max neval
#  DT[age > 5] 29.49726 29.93907 30.1813 30.67168 32.81204    10

所以在这个简单的例子中,data.table 比 which(...) 快两倍多,比 subset(...) 快 6 倍多。

【讨论】:

  • 注意:在 Windows 上的 R 3.3.1 64 位上运行,前三个选项之间的差异很小(平均值:110 - 114,中位数:77-87)。也许 R 处理子集的方式已经过优化?我没有尝试过 data.table,我认为它仍然更快。
【解决方案2】:

我重写代码,添加:



# 1. Libraries
library(microbenchmark)
library(data.table)
library(dplyr)

# 2. Reproducibility
set.seed(1)

# 3. Create data structures (1e6 rows)

# 3.1. Data frame 
df <- data.frame(
  age = sample(1:65, 1e6, replace = TRUE),
  x = rnorm(1e6),
  y = rpois(1e6,25))

# 3.2. Data table
dt <- as.data.table(df)

# 4. Helper functions

# 4.1. Function that uses standard evaluation
# http://adv-r.had.co.nz/Computing-on-the-language.html
subset2_q <- function(x, condition) {
  r <- eval(condition, x, parent.frame())
  x[r, ]
}

subset2 <- function(x, condition) {
  subset2_q(x, substitute(condition))
}

# 5. Benchmarks
microbenchmark(

  # 5.1. Data frame (basic operations)
  df[which(df$age > 5), ],
  df[df$age > 5, ],
  subset(df, age > 5),
  df[df[['age']] > 5, ],

  # 5.2. Data frame (dplyr)
  df %>% filter(age > 5),

  # 5.3. Data table (basic)
  dt[age > 5],
  dt %>% filter(age > 5), 

  # 5.4. Data frame and table with 'subset2'
  dt %>% subset2(age > 5),
  df %>% subset2(age > 5),

  # 5.5. How many times
  times = 10)

# Results

expr      min       lq      mean   median       uq       max neval cld
 df[which(df$age > 5), ] 83.07726 88.77624 102.20981 90.08606 91.52631 212.10305    10   b
        df[df$age > 5, ] 72.17319 79.98209  80.68900 81.42234 82.33832  84.46876    10   b
     subset(df, age > 5) 84.95796 85.90815  88.79125 88.03345 89.49680  95.37453    10   b
   df[df[["age"]] > 5, ] 71.39021 80.22755  81.86848 81.33061 82.38236 104.97732    10   b
  df %>% filter(age > 5) 21.37622 21.97020  23.57504 22.27681 25.17569  29.64354    10  a 
             dt[age > 5] 20.26226 20.55946  36.58179 25.24155 29.68587 143.57794    10  a 
  dt %>% filter(age > 5) 21.35613 21.76579  25.57424 22.02750 30.99570  32.18407    10  a 
 dt %>% subset2(age > 5) 20.41449 20.57485  23.93314 20.70827 28.63391  31.15306    10  a 
 df %>% subset2(age > 5) 77.43044 79.63956  92.24558 80.80100 81.61990 197.36958    10   b

data.tabledf %>% filter(age > 5) 运算符的结果最好。因此,带有 dplyr 的 data.frame 也很有用。

【讨论】:

    猜你喜欢
    • 2021-12-04
    • 1970-01-01
    • 2019-09-26
    • 2020-04-26
    • 1970-01-01
    • 2012-10-22
    • 1970-01-01
    • 2018-06-21
    • 2019-03-15
    相关资源
    最近更新 更多