【问题标题】:Counting how many times a condition is true within each group计算每个组中条件为真的次数
【发布时间】:2017-01-07 23:05:09
【问题描述】:

我正在使用一个包含多个组 (+2 百万) 的模拟数据集,我想在其中计算每个组的观察总数和高于阈值(这里为 2)的观察数。

当我创建一个标志变量时,它似乎要快得多,尤其是对于dplyr,而对于data.table,它要快一点。

为什么会这样?在每种情况下它是如何在后台工作的?

查看下面的示例。

模拟数据集

# create an example dataset
set.seed(318)

N = 3000000 # number of rows

dt = data.frame(id = sample(1:5000000, N, replace = T),
                value = runif(N, 0, 10))

使用 dplyr

library(dplyr)

# calculate summary variables for each group
t = proc.time()
dt2 = dt %>% group_by(id) %>% summarise(N = n(),
                                        N2 = sum(value > 2))
proc.time() - t

# user  system elapsed
# 51.70    0.06   52.11


# calculate summary variables for each group after creating a flag variable
t = proc.time()
dt2 = dt %>% mutate(flag = ifelse(value > 2, 1, 0)) %>%
  group_by(id) %>% summarise(N = n(),
                             N2 = sum(flag))
proc.time() - t

# user  system elapsed
# 3.40    0.16    3.55

使用 data.table

library(data.table)

# set as data table
dt2 = setDT(dt, key = "id")


# calculate summary variables for each group
t = proc.time()
dt3 = dt2[, .(N = .N,
              N2 = sum(value > 2)), by = id]
proc.time() - t

# user  system elapsed 
# 1.93    0.00    1.94 


# calculate summary variables for each group after creating a flag variable
t = proc.time()
dt3 = dt2[, flag := ifelse(value > 2, 1, 0)][, .(N = .N,
                                                 N2 = sum(flag)), by = id]
proc.time() - t

# user  system elapsed 
# 0.33    0.04    0.39 

【问题讨论】:

  • 对于data.tablesum(var).N 是gforce 优化的。但还没有sum(expr).. 添加verbose = TRUE 并查看优化表达式的差异。我们将在未来更好地捕捉这些案例。
  • 注意:你不再需要为分组/加入操作设置键了。如果你想键也可以。但你知道,没有必要。
  • data.table 我怀疑不同之处在于 data.table 已经在第二个实例中排序,所以你没有计算在内。尝试将dt2 = setDT(dt, key = "id") 放在每个实例上方。
  • @Arun 可能它还发挥作用,一种方法对所有行进行逻辑比较,而另一种方法通过分组进行?
  • 仅使用逻辑比较 (flag := value > 2) 将进一步提高速度。

标签: r data.table dplyr


【解决方案1】:

dplyr 的问题在于 sum 函数与表达式和大量 ID/组一起使用。 从 Arun 在 cmets 中所说的话,我猜 data.table 的问题很相似。

考虑下面的代码:我将其简化为说明问题所必需的最低限度。 dplyr 在对表达式求和时很慢,即使表达式只涉及恒等函数,所以性能问题与大于比较运算符无关。相比之下,dplyr 在对向量求和时速度很快。通过将 ID/组的数量从一百万减少到十个,可以实现更大的性能提升。

原因是 hybrid evaluation,即 C++ 中的求值,仅在 sum 与向量一起使用时才有效。使用表达式作为参数,评估在 R 中完成,这增加了每个组的开销。详细信息在链接的小插图中。从代码的profile看来,开销主要来自tryCatch错误处理函数。

##########################
### many different IDs ###
##########################

df <- data.frame(id = 1:1e6, value = runif(1e6))

# sum with expression as argument
system.time(df %>% group_by(id) %>% summarise(sum(identity(value))))
#    user  system elapsed
#  80.492   0.368  83.251

# sum with vector as argument
system.time(df %>% group_by(id) %>% summarise(sum(value)))
#    user  system elapsed
#   1.264   0.004   1.279


#########################
### few different IDs ###
#########################

df$id <- rep(1:10, each = 1e5)

# sum with expression as argument
system.time(df %>% group_by(id) %>% summarise(sum(identity(value))))
#    user  system elapsed
#   0.088   0.000   0.093

# sum with vector as argument
system.time(df %>% group_by(id) %>% summarise(sum(value)))
#    user  system elapsed
#   0.072   0.004   0.077


#################
### profiling ###
#################

df <- data.frame(id = 1:1e6, value = runif(1e6))

profvis::profvis({ df %>% group_by(id) %>% summarise(sum(identity(value))) })

代码简介:

【讨论】:

    猜你喜欢
    • 2015-03-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-09
    • 1970-01-01
    • 2017-08-28
    • 2014-10-01
    • 2011-12-27
    相关资源
    最近更新 更多