使用R

时间:2017-10-12 15:33:45

标签: r performance data.table

我需要在非常大的数据集(包含许多组)上执行类似于下面的操作,并在某处使用.SD读取速度很慢。有没有更快的方法来执行以下操作?

更准确地说,我需要在排除该组中的观察子集之后创建一个包含每个组的最小值的新列(类似于Excel中的minif)。

library(data.table)
dt <- data.table(valid = c(0,1,1,0,1),
                   a = c(1,1,2,3,4),
                   groups = c("A", "A", "A", "B", "B"))

dt[, valid_min := .SD[valid == 1, min(a, na.rm = TRUE)], by = groups]

输出:

> test
valid a k valid_min
1:     0 1 A         1
2:     1 1 A         1
3:     1 2 A         1
4:     0 3 B         4
5:     1 4 B         4

为了使其更复杂,组可能没有有效的条目,或者它们可能有多个有效但缺少的条目。我目前的代码与此类似:

dt <- data.table(valid = c(0,1,1,0,1,0,1,1),
                 a = c(1,1,2,3,4,3,NA,NA),
                 k = c("A", "A", "A", "B", "B", "C", "D", "D"))

dt[, valid_min := .SD[valid == 1, 
                      ifelse(all(is.na(a)), NA_real_, min(a, na.rm = TRUE))], by = k]

输出:

> dt
valid  a k valid_min
1:     0  1 A         1
2:     1  1 A         1
3:     1  2 A         1
4:     0  3 B         4
5:     1  4 B         4
6:     0  3 C        NA
7:     1 NA D        NA
8:     1 NA D        NA

2 个答案:

答案 0 :(得分:2)

有...

dt[dt[valid == 1 & !is.na(a), min(a), by=k], on=.(k), the_min := i.V1]

这应该很快,因为内部调用min是针对组优化的。 (见?GForce。)

答案 1 :(得分:1)

我们可以使用dplyr

执行相同操作
dt %>% 
  group_by(groups) %>% 
  mutate(valid_min = min(ifelse(valid == 1,
                                a, NA),
                         na.rm = TRUE))

给出了:

  valid     a groups valid_min
  <dbl> <dbl>  <chr>     <dbl>
1     0     1      A         1
2     1     1      A         1
3     1     2      A         1
4     0     3      B         4
5     1     4      B         4

或者,如果您对保持“无效”状态不感兴趣。我们可以执行以下操作:

dt %>% 
  filter(valid == 1) %>% 
  group_by(groups) %>% 
  mutate(valid_min = min(a))

看起来我提供了最慢的方法。比较每种方法(使用名为df的较大的复制数据框)和微基准测试:

library(microbenchmark)
library(ggplot2)
mbm <- microbenchmark(
  dplyr.test = suppressWarnings(df %>% 
                                  group_by(k) %>% 
                                  mutate(valid_min = min(ifelse(valid == 1,
                                                                a, NA),
                                                         na.rm = TRUE),
                                         valid_min = ifelse(valid_min == Inf,
                                                            NA,
                                                            valid_min))),


  data.table.test = df[, valid_min := .SD[valid == 1, 
                                          ifelse(all(is.na(a)), NA_real_, min(a, na.rm = TRUE))], by = k],
  GForce.test = df[df[valid == 1 & !is.na(a), min(a), by=k], on=.(k), the_min := i.V1]
)

autoplot(mbm)

enter image description here

......好吧,我试过......